Generics
7 pages · ~5 min✓ Reviewed
Builds on Exceptions. Next up: Collections Framework.
Generics Mental Model
1. What Generics Give
List<String>rejectsadd(42)at compile timeget()returnsString: no cast needed- Type arguments are reference types:
List<int>is illegal - Use
List<Integer>; autoboxing converts int values
2. Declaring Type Parameters
| Letter | Means |
|---|---|
T | type |
E | element |
K / V | key / value |
N | number |
- Generic class: parameter after the name
- Generic method: own parameter before the return type
Tis usually inferred from the arguments
java
class Box<T> { T val; } static <T> T first(List<T> l) { return l.get(0); }
first(names) infers T = String
3. Invariance
- Integerextends Number
- List<Integer>NOT a List<Number>
- List<? extends Number>accepts both
Subtype does not carry over
Only the raw and wildcard forms relate List<Integer> and List<Number>.
Bounded Types and Wildcards
1. Bounds on a Type Parameter
<T extends Comparable<T>>lets you call compareTo onT- Multiple bounds use
&, class first, then interfaces <T extends Number & Comparable<T>>
2. The Three Wildcards
| Type | Read gives | Write |
|---|---|---|
List<?> | Object | only null |
List<? extends Number> | Number | only null (compile error) |
Type Erasure Internals
1. What Erasure Does
- Compiler checks types, then erases the type arguments
Tbecomes its bound, orObject- Casts are inserted at call sites
List<String>andList<Integer>shareList.class, sogetClass()is equal
2. Bridge Methods
- compareTo(Object)synthetic bridge
- casts to Stringinserted by compiler
- compareTo(String)your method
- Bridges keep overriding working after erasure
3. Heap Pollution
Consequences of Erasure
1. What Erasure Bans
new T(),new T[10],T.class: T is gone at runtimestatic T field: one static shared by everyBox<X>catch (T e)andthrowof T: illegalinstanceof List<String>won't compile; useList<?>or plainList- Overloads differing only by type argument share one erasure
java
void f(List<String> a) {}
void f(List<Integer> a) {} // errorboth erase to f(List): name clash
2. Runtime Type Workarounds
- Pass a Class<T> token; use it to build and cast
- TypeToken: anonymous subclass keeps
List<String>in its superclass
Raw Types and Generic Arrays
1. Raw types
- Raw type:
Listwith no argument, checks off - Legacy only, never in new code
List<String> l = new ArrayList();unchecked warning- Raw
add(42)compiles, corrupts the list
java
List raw = strings; // List<String> raw.add(42); // compiles String s = strings.get(0); // CCE
2. Generic arrays
- Arrays check type at runtime, erasure can't
| Need | Illegal or risky |
|---|
Generics Cheat Sheet
1. Declare
- Class:
class Box<T>; method:<T>goes before the return type - Bound:
<T extends A & B>, class first, then interfaces after& - No primitives:
List<int>is illegal, use the wrapperList<Integer>
java
class Box<T extends Number & Comparable<T>> { T val; } static <T> T first(List<T> l) { return l.get(0); }
class bound and generic method
2. Wildcards and PECS
| Form | Read | Write | Role |
|---|---|---|---|
? |
Check yourself
Does this compile? What fixes it?
- No: generics are invariant.
- Declare
List<? extends Number> nums = ints;(read-only).
List<Integer> ints = new ArrayList<>(); List<Number> nums = ints;
What does this print?
true.- Erasure: both are the same
ArrayListclass at runtime.
List<String> a = new ArrayList<>(); List<Integer> b = new ArrayList<>(); System.out.println(a.getClass() == b.getClass());
Where does it fail?
- Compiles with warnings;
raw.add(42)succeeds. - Throws
ClassCastExceptionats.get(0), the cast the compiler inserted.
List<String> s = new ArrayList<>(); List raw = s; raw.add(42); String x = s.get(0);