Nearby lessons
81 of 125Java - Generics
- The problem generics solve
- Generic classes and generic methods
- Bounded type parameters
- Wildcards: ?, ? extends T, ? super T
- Type erasure — how generics work inside
Generics is a core concept of the Java language. This lesson explains The Problem Generics Solve, Generic Class and Generic Method with complete, runnable code examples, clear step-by-step explanations, and common mistakes to avoid with exam-style MCQs at the end.
The Problem Generics Solve
Before Java 5, a collection stored any kind of object. That meant you could accidentally add a String into a list of numbers, and the mistake would be discovered only at run time (crash), not at compile time.
Generics (Java 5+) fix this by letting you say clearly: this list can hold only Integers. The compiler then checks at compile time and stops mistakes early.
The Problem Generics Solve
Benefits: type safety (mistakes found at compile time), no casting (cleaner code), and self-documenting code (you can read what the list holds).
Generic Class
A generic class uses type parameters written in angle brackets <T>. T is a placeholder for the real type, decided when we use the class.
One class Box, many types — Box<String>, Box<Integer>, Box<Student>. The angle brackets < > are called the diamond operator (Java 7 lets us write new Box<>() without repeating the type).
Generic Method
A generic method has its own type parameter before the return type:
Bounded Type Parameters
Sometimes we want T to be restricted to a specific family — like only numbers so we can add them. We use extends to set an upper bound.
This gives us access to Number's methods (like doubleValue()), which plain T would not have.
Wildcards — ? , ? extends T , ? super T
Wildcards make generic methods more flexible. The ? means unknown type.
| Syntax | Meaning | Example use |
|---|---|---|
| ? | Any type | void show(List<?> list) — accepts a list of anything |
| ? extends T | T or its children (upper bound) | void sum(List<? extends Number> list) — accepts Integer, Double... |
| ? super T | T or its parents (lower bound) | void add(List<? super Integer> list) — accepts Integer or Object lists |
Type Erasure — How Generics Really Work
Here is a secret: generics exist only at compile time. At run time, Java erases all type information (this is called type erasure). The compiled .class file works with plain Object, and the compiler adds the casts for you.
That is why list instanceof List<String> is not allowed at run time — the <String> part simply does not exist then. The generics system protects you at compile time, then quietly steps aside at run time.
Common Generic Types You Already Use
Why Generics with Collections — Question and Answer
The classic material links generics tightly to collections. The two reasons:
- Type safety — without generics, a collection accepts anything, and mistakes are found only at run time. With generics, ArrayList<String> refuses a number at compile time.
- No casting — when you read from a collection, generics remove the manual cast: String s = list.get(0) instead of String s = (String) list.get(0).
That is why modern Java code always writes List<String>, Map<String, Integer> etc. — generics make collections safe and clean.
- Generics give compile-time type safety and remove manual casting.
- Generic class: class Box<T>; generic method: static <T> void m(...).
- Bounded types: <T extends Number> restricts T.
- Wildcards: ? = any, ? extends T = T or children, ? super T = T or parents.
- PECS: producers use extends, consumers use super.
- Type erasure: generics are compile-time only; at run time they are gone.