Runtime AtlasJava platform fieldbook
Java · Language to RuntimeView Markdown source

Generics and type safety

Generics let one type or method work with a family of types while preserving compile-time safety.

Parameterized types

List<String> means a list that accepts strings. Generic types are invariant: List<Integer> is not a subtype of List<Number>, because otherwise someone could add a Double to the integer list.

Bounds and wildcards

  • <T extends Comparable<T>>: a type parameter with an upper bound.
  • ? extends Number: producer of some unknown Number subtype.
  • ? super Integer: consumer that can safely accept Integer.
  • ?: unknown type when only Object-level behavior is required.

Remember PECS: Producer Extends, Consumer Super.

static double sum(List<? extends Number> values) { ... }
static void addDefaults(List<? super Integer> target) { ... }

Generic methods

The type parameter appears before the return type: static <T> T first(List<T> values). Type inference normally determines T.

Erasure

Most generic type arguments are erased from runtime representation. You cannot create new T(), use T.class, create generic arrays directly, or reliably test instanceof List<String>. Bridge methods may preserve polymorphism after erasure.

Raw types and heap pollution

Raw List bypasses generic checks and can move failure to a later cast. Varargs of generic types can create heap pollution; use @SafeVarargs only when the method is genuinely safe and eligible.

Feynman check

Generics are labels enforced at the warehouse entrance. Erasure means many labels are removed before delivery, so the runtime often sees only the box shape, not the original label.

Runtime AtlasIndependent study material · verify production details in official Java documentation