Generics
Source: Effective Java, 3rd Edition — Chapter 5 by Joshua Bloch (Items 26–29)
Since Java 5, generics let the compiler track element types — safer and clearer than casting from raw collections. This lesson: avoid raw types, eliminate unchecked warnings, prefer lists over arrays in generic code, and parameterize your own types.
Item 26: Don't use raw types
Terms:
| Term | Example |
|---|---|
| Generic type | List<E> |
| Formal type parameter | E |
| Parameterized type | List<String> |
| Actual type parameter | String |
| Raw type | List (generics erased) |
| Unbounded wildcard | List<?> |
Raw Collection stamps compiles but loses safety — stamps.add(new Coin()) may only fail at (Stamp) i.next(). Parameterized Collection<Stamp> catches the error at compile time.
Raw vs parameterized:
List— opts out of generics; unsafe.List<Object>— can hold any reference type; not a supertype ofList<String>(generics are invariant).List<?>— unknown element type; cannot add (exceptnull); safe for read-only algorithms.
For unknown element type, use Set<?>, not raw Set:
static int numElementsInCommon(Set<?> s1, Set<?> s2) { ... }
Legitimate raw-type uses: List.class (class literals); instanceof Set then cast to Set<?> — not raw Set.
Raw types and wildcards
1 / 2List<String> vs List<Object> for a parameter
Pick an answer to see if you are right.
Item 27: Eliminate unchecked warnings
Generics produce unchecked warnings: unchecked conversion, cast, method invocation, vararg. Fix them when you can — each warning is a potential runtime ClassCastException.
Easy fix example:
Set<Lark> exaltation = new HashSet<>(); // diamond, not raw HashSet
If you cannot eliminate a warning but can prove typesafety, suppress with @SuppressWarnings("unchecked") on the smallest scope (local variable, not whole class). Add a comment explaining why. Never suppress blindly — new real warnings get lost in the noise.
Unchecked warnings
1 / 2javac reports an unchecked warning on a legacy API call you cannot change, and you have verified by hand that only Strings ever flow through it
Pick an answer to see if you are right.
Item 28: Prefer lists to arrays
| Arrays | Generics | |
|---|---|---|
| Subtyping | Covariant (Long[] is Object[]) | Invariant (List<Long> ≠ List<Object>) |
| Runtime type | Reified — enforce element type | Erased — compile-time only |
Covariance bite:
Object[] a = new Long[1];
a[0] = "x"; // ArrayStoreException at runtime
List<Object> ol = new ArrayList<Long>() — won't compile (good).
No generic array creation: new List<String>() is illegal — would break type safety via erasure + covariance.
When mixing arrays and generics causes errors/warnings, prefer List<E> over E[]. Example: list-based Chooser<T> instead of (T[]) choices.toArray().
Arrays vs generics
1 / 2Object[] o = new Long[1]; o[0] = "hi"
Pick an answer to see if you are right.
Item 29: Favor generic types
Parameterize declarations and write generic types when casts would otherwise burden clients. Generify legacy types without breaking callers (migration compatibility, Item 26).
Generifying Stack: add <E>, replace Object with E. Backing array hits generic array creation — two fixes:
(E[]) new Object[n]in constructor + narrow@SuppressWarnings+ comment (common).Object[]storage + cast onpop()(cast at each read).
Prefer (1) when the array is private and only receives E from push. Primitives need boxed types (Stack<Integer>, not Stack<int>).
Bounded type parameters: DelayQueue<E extends Delayed> restricts E so clients need no casts.
Items 26–29 map
| Item | Move |
|---|---|
| 26 | No raw types; Set<?> for unknown element types |
| 27 | Fix unchecked warnings; suppress only with proof + narrow scope |
| 28 | Lists over arrays in generic APIs; arrays covariant, generics invariant |
| 29 | Parameterize types; generify legacy APIs; bounded params when needed |
Next: Wildcards and Type Tokens (Items 30–33).