Appearance
Lesson 02 · Generics
Objectives
After this lesson you will be able to:
- Declare generic classes and methods with type parameters and bounds.
- Use wildcards (
?,extends,super) and apply PECS. - Explain type erasure and its consequences.
Type parameters and generic methods
A type parameter (<T>) makes a class or method type-safe without casts.
java
class Box<T> {
private T value;
void set(T v) { value = v; }
T get() { return value; }
}
static <T> T first(List<T> list) { return list.get(0); } // generic methodBounded type parameters
A bound constrains the type argument. extends works for both classes and interfaces (there's no super bound on a type parameter).
java
static <T extends Comparable<T>> T max(List<T> xs) { // T must be Comparable
T best = xs.get(0);
for (T x : xs) if (x.compareTo(best) > 0) best = x;
return best;
}
static <T extends Number & Cloneable> void f(T t) { } // multiple bounds with &Wildcards and PECS
A wildcard ? is an unknown type, used on variables/parameters (not on declarations):
List<? extends T>— an upper-bounded producer: you can readTbut can't add (exceptnull).List<? super T>— a lower-bounded consumer: you can addTbut reads come back asObject.List<?>— unbounded; read asObject, can't add (exceptnull).
The mnemonic is PECS — Producer extends, Consumer super.
java
double sum(List<? extends Number> nums) { // produces Numbers (read)
double t = 0; for (Number n : nums) t += n.doubleValue(); return t;
}
void addInts(List<? super Integer> sink) { // consumes Integers (write)
sink.add(1); sink.add(2);
}Exam trap
You cannot add to a List<? extends Number> (the compiler can't know the exact element type) — list.add(1) is a compile error, though list.add(null) is allowed. You can add Integers to a List<? super Integer>.
Type erasure
Generics are a compile-time feature. At runtime the type argument is erased — List<String> and List<Integer> are both just List. Consequences the exam tests:
java
new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass(); // true
// list instanceof List<String> // does NOT compile — no runtime generic type
// new T[10] / new T() // illegal — type parameter has no runtime formGotcha
Because of erasure you can't create new T[], do obj instanceof List<String>, or have two overloads that differ only by generic type (f(List<String>) vs f(List<Integer>) clash). A raw List (no type argument) compiles with an unchecked warning and defeats type safety.
Key Takeaways
- Type parameters (
<T>) give compile-time type safety; generic methods put<T>before the return type. - Bounds use
extends(<T extends Comparable<T>>, multiple bounds with&); there's nosuperbound on a type parameter. - PECS:
? extends Tto read (can't add),? super Tto write (reads asObject). - Type erasure removes generics at runtime: no
new T[], noinstanceof List<String>, no generic-only overloads; allList<X>share oneClass.
Lesson Quiz
Which operation is ILLEGAL on List<? extends Number> nums?
PECS stands for...
What does new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass() return?
Which is a valid bounded type parameter?
Why won't obj instanceof List<String> compile?
Next: Core Collections. Run the matching code in labs/src/main/java/com/jse21/m05_collections/.