Generics
Why Generics
Generics move type errors from runtime to compile time: a
List<String> can only ever hold strings, and reading from it needs no cast. Raw types exist only for backward compatibility — never write them.- Pre-generics collections held
Object— every read needed a cast that could fail at runtime - Type parameters document intent and are enforced by the compiler
- The diamond
<>infers type arguments:new ArrayList<>() - Raw types (
Listwithout<...>) silently opt out of type checking (EJ 26) List<?>is the safe "list of unknown" — rawListis not
// Pre-Java-5: trust, then ClassCastException at a distance
List files = new ArrayList();
files.add("readme.txt");
files.add(42); // compiles!
String f = (String) files.get(1); // BOOM — at runtime, far from the bug
// Generics: the compiler rejects the bad add
List<String> names = new ArrayList<>();
names.add("readme.txt");
// names.add(42); // compile error — bug caught at source
String first = names.get(0); // no castGenerics also power the fluent APIs you use daily: Stream<T>, Optional<T>, CompletableFuture<T>, Map<K,V>. Understanding variance (Wildcards Pecs) and Type Erasure explains both their signatures and their few sharp edges.