Generics: Type-Safe, Reusable Code
Introduction
Generics let you write classes, interfaces, and methods that work with type parameters—placeholders for real types checked at compile time. They power List<String>, Map<K, V>, and your own reusable APIs. Learn generics here before Collections and Interfaces.
Prerequisites
Why Generics Matter
Without generics, collections hold Object and require casts:
// Legacy style — avoid in new code
java.util.List list = new java.util.ArrayList();
list.add("Java");
String s = (String) list.get(0); // cast + risk at runtimeWith generics:
java.util.List<String> list = new java.util.ArrayList<>();
list.add("Java");
String s = list.get(0); // no cast — compile-time checkBenefits:
- Type safety — wrong types caught at compile time
- Fewer casts — cleaner code
- Clearer APIs —
List<Student>documents intent
1) Generic Class
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}Box<String> nameBox = new Box<>();
nameBox.set("Alice");
System.out.println(nameBox.get());
Box<Integer> intBox = new Box<>();
intBox.set(42);T is a type parameter. Each specialization (Box<String>, Box<Integer>) is a distinct type at compile time.
2) Diamond Operator <>
Since Java 7, omit repeated type on the right when the compiler can infer:
Box<String> box = new Box<>(); // diamond infers String3) Generic Interface
Interfaces can be generic too—List<E>, Comparable<T>:
public interface Repository<T> {
void save(T item);
T findById(long id);
}You will see many in the collections framework (Collections Overview).
4) Generic Methods
A method can have its own type parameter:
public static <T> void printTwice(T value) {
System.out.println(value);
System.out.println(value);
}printTwice("Hello");
printTwice(123);Type inference often picks T from arguments.
5) Bounded Type Parameters
Restrict T to a subtype:
public static <T extends Number> double sum(T a, T b) {
return a.doubleValue() + b.doubleValue();
}T extends Number means T must be Number or a subclass (Integer, Double, …).
6) Wildcards (Introduction)
Wildcards add flexibility when you read or write collections with unknown type parameters.
? extends T — upper bound (read-oriented)
public static double sumNumbers(List<? extends Number> numbers) {
double total = 0;
for (Number n : numbers) {
total += n.doubleValue();
}
return total;
}Accepts List<Integer>, List<Double>, etc.
? super T — lower bound (write-oriented, e.g. add)
public static void addIntegers(List<? super Integer> list) {
list.add(42);
}Accepts List<Integer>, List<Number>, List<Object>.
Tip
PECS Rule (helpful mnemonic)
Producer extends, Consumer super — if you mainly read (? extends), if you mainly write (? super). Full mastery comes with practice; collections chapters reinforce this.
Do not overuse wildcards in beginner code—List<String> is enough until APIs require flexibility.
7) Generics and Primitives
Generics work with reference types only. Primitives use wrappers:
List<int> bad; // compile error
List<Integer> good; // OK — autoboxingSee Numbers for boxing/unboxing.
Common Beginner Mistakes
Raw Types
List list = new ArrayList(); // raw — loses type checksAlways supply type arguments in new code: List<String> list = new ArrayList<>();
Creating Generic Array
new T[10] is not allowed (type erasure). Use ArrayList<T> instead.
Assuming Generics Exist at Runtime
Java erases type parameters at runtime for compatibility. Reflection on T is limited—advanced topic.
Mini Practice
- Create
Pair<A, B>withfirstandsecondgetters - Write
static <T> T firstOrDefault(List<T> list, T defaultValue)
What’s Next
Fixed constant sets with Enums, then class hierarchies with Inheritance.
FAQ
What is type erasure?
Compile-time checks use generics; bytecode often uses Object or bounds. You trade some runtime type info for backward compatibility.
Can I use List<Object> instead of List<String>?
No for assignment safety: List<String> is not a subtype of List<Object> in Java (invariance). Use wildcards or generics properly.
When do I need wildcards?
When writing library methods that accept “any list of some Number” without fixing one type parameter. Start simple; add wildcards when APIs demand them.
Are arrays or generics better for beginners?
Arrays for fixed size and primitives; generic collections for dynamic size and type-safe APIs.
How does this connect to Arrays.sort in Ranking Game?
The comparator uses generic types (Student) so the compiler checks comparison logic against the right class.