BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Generics and functional

Java language

Generics

Generics put a type in angle brackets, as in List<String>, so the compiler checks what a container holds and you need no casts.

๐Ÿ”Ž Unravel this example

At a glance

Mental model

A labelled box: a "Box of Apples" cannot accidentally receive a screwdriver, and anything you take out is known to be an apple.

What happens, in order
  1. 1List<String> says every element is a StringMap<String, List<Order>> nests the idea: names mapped to lists of orders.
  2. 2Methods can have their own type parameter: <T> T first(List<T> list)The compiler infers T from the arguments.
  3. 3The diamond new ArrayList<>() lets the compiler fill in the type from the left sidevar lets it infer the variable type from the right side.
Remember
Watch out
Raw types (List without type) that disable checksAssuming List<Dog> is a List<Animal>Trying to create generic arrays or new T()
Example (highlighted lines explain it)
public static <T extends Comparable<T>> T largest(List<T> items) {    T best = items.get(0);    for (T item : items) {        if (item.compareTo(best) > 0) {            best = item;        }    }    return best;}
Read the full explanation (about 1 min)

Generics put a type in angle brackets, as in List<String>, so the compiler checks what a container holds and you need no casts.

A mental model

A labelled box: a "Box of Apples" cannot accidentally receive a screwdriver, and anything you take out is known to be an apple.

How it works

List<String> says every element is a String. Map<String, List<Order>> nests the idea: names mapped to lists of orders.

Methods can have their own type parameter: <T> T first(List<T> list). The compiler infers T from the arguments.

The diamond new ArrayList<>() lets the compiler fill in the type from the left side. var lets it infer the variable type from the right side.

Going deeper

Generics exist only at compile time (type erasure). At runtime List<String> and List<Integer> are both just List, so you cannot write new T() or instanceof List<String>.

Wildcards express flexibility: List<? extends Number> can be read as numbers but not added to; List<? super Integer> can be added to. Rule of thumb: "producer extends, consumer super".

A List<Dog> is not a List<Animal>, even though a Dog is an Animal; otherwise you could add a Cat through the Animal view.

Under the hood

The compiler checks the types, inserts casts where needed and erases the type arguments, which is why old code without generics still runs. Bridge methods are generated to keep overriding working.

Common mistakes

  • Raw types (List without type) that disable checks.
  • Assuming List<Dog> is a List<Animal>.
  • Trying to create generic arrays or new T().

How Unravel recognises it

When you paste code, Unravel looks for these signs of generics. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Builds on this

Frequently asked questions

What is Generics?

Generics put a type in angle brackets, as in List<String>, so the compiler checks what a container holds and you need no casts.

What are the most common mistakes with Generics?

Raw types (List without type) that disable checks. Assuming List<Dog> is a List<Animal>. Trying to create generic arrays or new T().

How can I see Generics in real code?

Paste your own Java into Unravel and it will point out where generics appears, explain how it works at the detail level you choose, and draw the flow of the code around it.