Generics
Generics Best Practices
The Effective Java generics doctrine in one place: no raw types, no unchecked warnings, lists over arrays, generify your own APIs, PECS at boundaries, and type tokens when runtime types are unavoidable.
- Don't use raw types (26); eliminate unchecked warnings (27)
- Prefer lists to arrays in APIs (28)
- Favor generic types (29) and generic methods (30)
- Bounded wildcards for flexible APIs — PECS (31)
- Combine generics and varargs judiciously —
@SafeVarargs(32) - Typesafe heterogeneous containers via
Class<T>keys (33)
| Item | Rule | Sound bite |
|---|---|---|
| 26 | No raw types | Raw types opt out of the entire type system |
| 27 | No unchecked warnings | Every warning is a deferred ClassCastException |
| 28 | Lists over arrays | Fail at compile time, not at runtime |
| 29 | Generify your classes | Casts in client code are your API's failure |
| 30 | Generify your methods | Inference makes them free to call |
| 31 | Bounded wildcards | PECS: producer-extends, consumer-super |
| 32 | Varargs with care | @SafeVarargs, or take a List instead |
| 33 | Type tokens | Class<T> keys make containers heterogeneous AND safe |
public class Favorites {
private final Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void put(Class<T> type, T instance) {
favorites.put(Objects.requireNonNull(type), type.cast(instance));
}
public <T> T get(Class<T> type) {
return type.cast(favorites.get(type)); // dynamic cast — provably safe
}
}
Favorites f = new Favorites();
f.put(String.class, "Java");
f.put(Integer.class, 42);
String s = f.get(String.class); // no cast in client codeThe container works because the key carries the type: Class<T> is a parameterized type token, and Class.cast is a checked, type-safe cast. The same idea powers annotation lookups (getAnnotation(Class<A>)) and dependency-injection frameworks.