Generics and Collections: Type Safety in Practice
Generics and Collections: Type Safety in Practice
Generics let you write collection code that is checked at compile time. Instead of storing Object and casting everywhere, you declare List<String> and let the compiler guarantee that everything in the list really is a string. Raw types, unchecked casts, and wildcard confusion are the three things most likely to trip up developers.
Parameterized collections
A parameterized type such as Map<String, List<Integer>> documents your data shape in the type system. The compiler inserts invisible casts for you when you read from the collection, so a wrong assumption fails at compile time instead of blowing up with ClassCastException at runtime.
List<String> names = new ArrayList<>();
names.add("Ada");
// names.add(42); // compile error - rejected upfront
String first = names.get(0);
Wildcards and variance
Wildcards model variance. List<? extends Number> is a producer you can read from as a number. List<? super Integer> is a consumer you can safely add integers to. The mnemonic PECS - producer extends, consumer super - keeps both directions straight.
Raw types are a trap
A raw List bypasses all the safety generics give you. Mixing raw and parameterized types produces unchecked warnings that hint at future failures. Treat every unchecked warning as a potential runtime bug and audit them with a linter before shipping.
Modern syntax
Java 9 and later provide handy immutable factories: List.of, Set.of, and Map.of create compact, unmodifiable collections that reject nulls and work beautifully in combination with generics.
Key Points
- Generics make collections compile-time safe and remove manual casting.
- Use the diamond operator
<>where inference is obvious. - Use wildcards only in method signatures, following the PECS rule.
- Raw types disable type safety; treat warnings seriously.
- Prefer
List.of,Set.of, andMap.offor immutable data.