Clean • Professional
Generics in Java enable type-safe programming by allowing classes, interfaces, and methods to work with different types of objects while ensuring compile-time type checking. Introduced in Java 5, generics improve code reusability, readability, and maintainability.
Generics let you define classes, interfaces, and methods with a type parameter (T, E, K, V), which is replaced with a concrete type when used.
Example:
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // Compile-time error
Without generics, ArrayList could store any Object, which may cause runtime errors.

ClassCastException.A generic class works with any object type.
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
// Usage
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello Generics");
System.out.println(stringBox.getContent());
Box<Integer> intBox = new Box<>();
intBox.setContent(123);
System.out.println(intBox.getContent());
Here, T is a type parameter replaced with a concrete type (String, Integer) at runtime.
Methods can also have type parameters independent of the class:
public class Utility {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
// Usage
Integer[] intArr = {1, 2, 3};
String[] strArr = {"A", "B", "C"};
Utility.printArray(intArr);
Utility.printArray(strArr);
Sometimes, you want to restrict generic types.

1. Upper Bounded (extends)
Limits type to a class or interface.
public class Calculator<T extends Number> {
public double square(T number) {
return number.doubleValue() * number.doubleValue();
}
}
T can only be a subclass of Number (Integer, Double, Float).
2. Lower Bounded (super)
Used mainly in method parameters.
public void addNumbers(List<? super Integer> list) {
list.add(10);
}
Accepts a list of Integer or its superclasses (Number, Object).
Wildcards (?) allow flexibility in method parameters:
List<?> list = new ArrayList<String>(); // Unknown type
<? extends Number> → accepts Number or subclasses<? super Integer> → accepts Integer or superclasses<?> → accepts any type| Wildcard | Meaning | Example |
|---|---|---|
<?> | Any type | List<?> list |
<? extends T> | T or subclass | List<? extends Number> |
<? super T> | T or superclass | List<? super Integer> |
Tips:
extends for reading (covariance).super for writing (contravariance).Generics are heavily used in collections:
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
Map<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
Advantages:
Generics work seamlessly with interfaces and inheritance:
interface Container<T> {
void add(T item);
T get(int index);
}
class Box<T> implements Container<T> {
private List<T> items = new ArrayList<>();
public void add(T item) { items.add(item); }
public T get(int index) { return items.get(index); }
}
class FancyBox<T> extends Box<T> {
void fancyDisplay() { System.out.println("Fancy Box: " + get(0)); }
}
Generics in Java use type erasure, which means:
// Error
T[] array = new T[10];
// Use this instead
@SuppressWarnings("unchecked")
T[] array = (T[]) new Object[10];
List vs List<String>).== instead of .equals() for comparison.new T()).List<T>, Set<T>, Map<K, V>Box<T>, Pair<K, V>Comparator<T>Generics in Java: