Generics in Java are a cornerstone of type-safe and reusable code. They allow developers to create classes, methods, and interfaces that can work with any type of data, making the code more robust and flexible. In this article, we’ll explore the concept of generics with an example of a shopping cart that can hold different types of fruits, modeled as custom classes.
Why Use Generics? The Problem with Arrays
In Java, arrays are type-specific, which means an array of one type cannot hold elements of another type:
String[] fruits = new String[3];
fruits[0] = "Apple"; // Valid
fruits[1] = 123; // Compile-time error
While this type enforcement is useful, arrays are limited in flexibility. For instance, if we want to create a shopping cart that can hold objects representing various fruits—like bananas, apples, and grapes—arrays would require significant manual handling.
Generics solve this problem by allowing us to define flexible yet type-safe data structures. Let’s implement this concept step-by-step, starting with creating our custom Fruit
classes.
Step 1: Defining the Fruit Classes
We’ll create a base class Fruit
and three specific fruit classes: Banana
, Apple
, and Grape
. Each class will have some unique properties.
// Base class for all fruits
public abstract class Fruit {
private String name;
public Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// Specific fruit classes
public class Banana extends Fruit {
public Banana() {
super("Banana");
}
}
public class Apple extends Fruit {
public Apple() {
super("Apple");
}
}
public class Grape extends Fruit {
public Grape() {
super("Grape");
}
}
Why an abstract class ?
We chose an abstract class for
Fruit
to provide shared functionality (e.g., storing a name) and represent a clear hierarchical relationship. However, in cases where multiple inheritance or simpler contracts are needed, an interface could be a better choice. This topic is broad enough to merit its own exploration!
Step 2: Implementing a Generic Shopping Cart
Now, we’ll create a ShoppingCart
class that uses generics. This class can hold any type of fruit while ensuring type safety.
import java.util.ArrayList;
public class ShoppingCart<T extends Fruit> { // Ensures only Fruit or its subclasses are allowed, if you want a more generic cart remove it
private ArrayList<T> items = new ArrayList<>();
public void addItem(T item) {
items.add(item);
}
public void removeItem(T item) {
items.remove(item);
}
public void displayItems() {
for (T item : items) {
System.out.println(item.getName());
}
}
}
By specifying T extends Fruit
, we restrict the generic type T
to the Fruit
class or its subclasses. This prevents unrelated objects from being added to the cart.
Step 3: Using the Shopping Cart
Let’s see how we can use the ShoppingCart
class with our custom fruit objects.
public class Main {
public static void main(String[] args) {
ShoppingCart<Fruit> fruitCart = new ShoppingCart<>();
// Adding fruits to the cart
fruitCart.addItem(new Banana());
fruitCart.addItem(new Apple());
fruitCart.addItem(new Grape());
// Displaying the cart's contents
System.out.println("Shopping Cart Items:");
fruitCart.displayItems();
// Removing an item
fruitCart.removeItem(new Apple()); // Only removes if the object matches (equals method can be overridden for advanced handling)
System.out.println("After removing Apple:");
fruitCart.displayItems();
}
}
Benefits of Using Generics
-
Type Safety: By restricting
T
toFruit
or its subclasses, we prevent adding invalid types. -
Flexibility: The shopping cart can hold any type of
Fruit
, making it reusable for various objects. - Elimination of Casting: There’s no need to cast objects when retrieving them from the cart, reducing runtime errors.
Conclusion
Using generics with custom classes, like our Fruit
hierarchy, showcases the flexibility and power of this Java feature. The ShoppingCart
class demonstrates how we can enforce type safety while maintaining a reusable and flexible structure.
This example is not only practical but also highlights how generics help in writing cleaner and more maintainable code. Whether you're building a real application or learning the fundamentals, generics are an indispensable tool for any Java developer.
Top comments (1)
This is not demonstration of Java generics.
The use of a generic in ShoppingCart is unnecessary and could be done without the generic just by using Fruit.
The reason the code works is because of inheritance, not the use of a generic. The Fruit subclasses and so they can be added to the ArrayList. An identical implementation could be achieved using a Fruit[] array.