Skip to main content

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:

TermExample
Generic typeList<E>
Formal type parameterE
Parameterized typeList<String>
Actual type parameterString
Raw typeList (generics erased)
Unbounded wildcardList<?>

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 of List<String> (generics are invariant).
  • List<?> — unknown element type; cannot add (except null); 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.

Quiz

Raw types and wildcards

1 / 2

List<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.

Quiz

Unchecked warnings

1 / 2

javac 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​

ArraysGenerics
SubtypingCovariant (Long[] is Object[])Invariant (List<Long> ≠ List<Object>)
Runtime typeReified — enforce element typeErased — 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().

Quiz

Arrays vs generics

1 / 2

Object[] 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:

  1. (E[]) new Object[n] in constructor + narrow @SuppressWarnings + comment (common).
  2. Object[] storage + cast on pop() (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​

ItemMove
26No raw types; Set<?> for unknown element types
27Fix unchecked warnings; suppress only with proof + narrow scope
28Lists over arrays in generic APIs; arrays covariant, generics invariant
29Parameterize types; generify legacy APIs; bounded params when needed

Next: Wildcards and Type Tokens (Items 30–33).