ConceptDifficulty: Intermediate1 min

Generics: Moving a Type Error From Run Time to Compile Time

A List<String> refuses the line that puts in the wrong thing, instead of failing on the line that reads it.

Generics let a class take a type as a parameter, as in List<String>: a list declared to hold strings. The compiler then checks every line that adds to the list, so a wrong element is refused where it is written instead of failing later where it is read.

Before generics, every collection held Object. Taking something out meant casting it back, and the cast was a promise nobody checked until the line ran.

java
List refs = new ArrayList();
refs.add("AB-1");
refs.add(42);

String first = (String) refs.get(1);
compiles cleanly, throws on line 5

In that listing, line 3 is the mistake and line 5 is where it surfaces. In a real program those two lines can sit in different classes, written months apart by different people, and the stack trace names only the second.

With a type parameter, the compiler knows what belongs in the list:

java
List<String> refs = new ArrayList<>();
refs.add("AB-1");
refs.add(42);

String first = refs.get(0);
line 3 no longer compiles, and line 5 needs no cast
What changes when the type is a parameter
Compared onListList<String>
Wrong thing addedAcceptedRefused by the compiler
Taking something outNeeds a castNeeds nothing
Where a mistake is foundAt the line that reads itAt the line that wrote it

So generics do not make anything faster and do not change what is stored. They move a failure from the machine of whoever runs your code to the machine of whoever writes it, which is the only place a failure is cheap.