Chapter 12 of 16Foundation

Generics and Optional

Parameterize APIs for type safety, use bounds and wildcards correctly, and represent an absent return value explicitly.

35 minJava 21
01

Why it matters

Start from the concept, not the syntax

Meet a concrete situation where this idea earns its place, then state the boundary it guards.

Generics let one piece of code work on many types while the compiler checks every use. A single Box<T> or List<String> replaces copy-pasted versions and pushes ClassCastException from runtime back to compile time, where it belongs.

By the end of this chapter

Raw types and wrong wildcards push ClassCastException back to runtime and defeat the safety generics exist to provide.

  • Parameterized types
  • Bounded parameters
  • PECS wildcards
02

Mechanism

How Java expresses the concept

Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.

The mental model

A generic class or method parameterizes a type: `Box<T>` holds a `T`, and `List<String>` promises only strings. This moves a whole class of `ClassCastException` errors from runtime to compile time.

Type parameters can be bounded: `<T extends Number>` accepts `Number` and its subclasses. Wildcards express flexibility: `? extends T` for reading (producer), `? super T` for writing (consumer). This is the PECS rule: Producer Extends, Consumer Super.

Generics are erased at runtime: `List<String>` and `List<Integer>` are the same class at runtime, so you cannot use `T` in `new T[...]` or `instanceof`. The diamond operator `<>` lets the compiler infer the type arguments so you avoid repeating them.

`Optional<T>` is primarily a return type for a method that may have no result. It should not itself be `null`, and it is not a replacement for every nullable field or parameter.

Bounded types and wildcards are easy to overuse. Start with a single `<T>`; add bounds only when a method truly needs to call a supertype method or accept a family of subtypes.

03

Concept map

Inspect the building blocks as a whole

Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.

Core Java

Generics and type safety

Generics let one class or method work on many types while keeping compile-time type checks and avoiding casts.

  1. 01Parameterized types
  2. 02Compile-time safety
  3. 03Bounded type parameters
  4. 04PECS wildcards
  5. 05Type erasure
  6. 06Diamond operator
  7. 07Optional return values
04

Implementation

Encode the smallest runnable example

Write and run the code, then observe the behavior you just predicted with a real Java program.

Runnable snippet path

Predict, run, and change each example

2 focused examples
01

A generic box

One class serves any type while keeping the type checked at compile time.

Box.java
public class Box<T> {
    private T value;
    void set(T value) { this.value = value; }
    T get() { return value; }
}

class Use {
    static void demo() {
        Box<String> b = new Box<>();
        b.set("hi");
        String s = b.get(); // no cast
    }
}

Remember

Use the diamond <> so the compiler infers the type argument for you.

Change one input, predict the result, then compile or test it before continuing.

02

PECS wildcard

A consumer that adds Ts uses super; a producer that reads uses extends.

Copy.java
import java.util.*;

class Copy {
    static <T> void copy(List<? super T> dest, List<? extends T> src) {
        dest.addAll(src);
    }
}

Remember

Producer Extends, Consumer Super: it prevents adding the wrong type.

Change one input, predict the result, then compile or test it before continuing.

05

Watch out

Break it on purpose, then repair the right layer

Predict the symptom before running the failure case, and close with the completion checklist.

Deliberate failure

  1. Break

    Use a raw List and cast elements on the way out

  2. Observe

    A ClassCastException is thrown far from where the bad value entered

  3. Repair

    Keep the type parameter, use the diamond operator, and apply PECS when a method both reads and writes

Why it matters

Keep the type parameter, use the diamond operator, and apply PECS when a method both reads and writes

Completion checklist

  • Use a type parameter for reusable code
  • Bound a parameter when a method needs supertype behavior
  • Apply extends and super wildcards correctly
06

Go deeper

Official references

The primary sources behind this chapter. Every claim above stays consistent with them.