Memra

Why generics: type safety and the diamond operator

◈ 4 cards

How generics replaced raw types — type-checked at compile time, zero runtime cost.

The problem generics solve

Before Java 5, collections held Object references. You had to cast on every read, and a wrong cast compiles silently but blows up at runtime:

// Raw type — pre-generics style (avoid this)
List names = new ArrayList();
names.add("Alice");
names.add(42);                  // compiles — no type check!
String s = (String) names.get(1); // ClassCastException at runtime

Generics bind a type parameter to a collection so the compiler enforces the constraint:

List<String> names = new ArrayList<String>();
names.add("Alice");
// names.add(42); // compile error — int is not String
String s = names.get(0);   // no cast needed

The diamond operator <> (Java 7+) lets the compiler infer the type argument from the declared type, so you don't repeat yourself:

List<String>         names  = new ArrayList<>();  // diamond: infers String
Map<String, Integer> counts = new HashMap<>();    // infers both params

Generics are compile-time only: the bytecode has no type arguments (this is called type erasure, covered at the end of this module). At runtime both List<String> and List<Integer> are just List.

statementList namesList<String> namesnames.add("Alice")acceptedacceptednames.add(42)acceptedcompile errorreading an elementcast requiredno castat runtimeClassCastExceptionsafeSame code, one type argument apart.
The type argument moves the failure from runtime to the add() call. With the raw type nothing complains until the cast, which is usually far away from the line that inserted the wrong value.
NORMAL ~/memra/learn/java-from-zero/why-generics utf-8 LF