Java Generics
After this lesson you'll write type-safe, reusable containers and methods — the same machinery that powers Java's collections — and you'll know exactly when to reach for a wildcard.
Learn Java Generics in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Think of a generic class as a labelled shipping container . The container itself is the same design no matter what goes inside — but when you fill it, you slap a label on the door: "Books only" or "Glassware only."
The label is the type argument. A container labelled Box Glass will refuse a crate of books at the loading dock — the compiler is the dock worker checking the label. Without a label (a "raw type"), anything goes in, and you only discover the smashed glass when you open it later (a runtime crash). Generics move that check from opening the box (runtime) to loading the box (compile time).
Before generics, collections held Object , so they accepted anything — and you had to cast on the way out. One wrong assumption and your program crashed at runtime.
Key benefit: a bug caught at compile time is far cheaper than one your users find at runtime.
A generic class declares one or more type parameters in angle brackets after its name. Inside the class, those parameters act like real types; they get filled in when you create an instance.
Naming convention: T = Type, E = Element, K = Key, V = Value, N = Number. The on the right is the "diamond" — Java infers the type, so you don't repeat it.
A method can have its own type parameter, even inside a non-generic class. You declare it in angle brackets just before the return type . Java infers the actual type from the arguments you pass.
T extends X means "T must be X, or a subclass/implementer of X." This unlocks X's methods on T. (Note: for both classes and interfaces you write extends , never implements .)
Fill in the two blanks so Holder becomes a generic class that stores and returns a value of any type. Declare the type parameter, then give get() the right return type.
Complete the generic min method. It needs a Comparable bound so it can call compareTo , and the right comparison operator to pick the smaller value.
A wildcard ? means "some specific but unknown type." It lets a method accept a whole family of generic types instead of one exact type.
PECS Rule: Producer Extends, Consumer Super. If the parameter produces values you read, use extends . If it consumes values you write, use super .
Java generics are a compile-time feature only . After the compiler checks your types, it erases them — replacing each type parameter with its bound (or Object if unbounded) and inserting casts. At runtime the JVM has no idea what T was.
This is why List String and List Integer are literally the same class when the program runs. Erasure keeps generics backwards-compatible with pre-2004 Java, but it imposes real limits:
Workarounds: pass a Class T token to create instances, and prefer an ArrayList T over a raw generic array.
Time to write one from scratch. Read the brief in the comments, then fill in the body yourself — no blanks to lean on this time.
Writing List list = new ArrayList(); drops all type safety and triggers an "unchecked" warning. Always parameterise:
Type erasure means there's no T at runtime. Pass a factory or a class token instead:
You can't create an array of a type parameter. Use a collection, or create an Object[] and cast (with a warning):
❌ "incompatible types" — adding to a ? extends list
This is wildcard-capture confusion. You can't add to a ? extends list because the exact subtype is unknown. Read from extends ; write to super :
You can now write generic classes ( Box T , Pair K, V ) and generic methods ( T T pick(...) ), constrain them with bounds, choose wildcards using PECS, and explain why type erasure forbids new T() and generic arrays. This is the exact toolkit behind Java's collections and APIs.
Next up: Advanced Methods — overloading, varargs, method references, and recursion.
Practice quiz
In 'class Box<T>', what is T?
- A reserved keyword
- A primitive type
- A type parameter (placeholder for some type)
- A variable name
Answer: A type parameter (placeholder for some type). T is a type parameter, filled in when you create a Box like Box<String>.
Where do you declare the type parameter of a generic METHOD?
- Just before the return type
- After the method name
- Inside the parameter list
- On the class only
Answer: Just before the return type. A generic method declares <T> just before its return type, e.g. static <T> T pick(...).
By convention, which letter is used for the type parameter of a key?
- T
- E
- N
- K
Answer: K. Convention: T=Type, E=Element, K=Key, V=Value, N=Number.
What does '<T extends Comparable<T>>' allow you to do inside the method?
- Create new T objects
- Call compareTo on a T
- Make a T array
- Cast T to int
Answer: Call compareTo on a T. The bound guarantees every T implements Comparable, so compareTo is allowed.
Per PECS, which wildcard do you use for a list you only READ from?
- ? extends T
- ? super T
- raw type
- ? equals T
Answer: ? extends T. Producer Extends: use ? extends T when the list produces values you read.
Why can't you write 'new T()' inside a generic class?
- It is too slow
- T must be final
- Type erasure removes T at runtime
- It needs an import
Answer: Type erasure removes T at runtime. Type erasure means there is no T at runtime, so the JVM cannot instantiate it.
At runtime, List<String> and List<Integer> are...
- Completely different classes
- The same class (erasure)
- Subclasses of each other
- Not real classes
Answer: The same class (erasure). After type erasure they are literally the same class at runtime.
Can you add elements (other than null) to a List<? extends Number>?
- Yes, any Number
- Only Integers
- Only if it is empty
- No — the exact subtype is unknown
Answer: No — the exact subtype is unknown. You cannot add to a ? extends list because the exact subtype is unknown; you can only read.
Do generics make a program run faster or use less memory?
- Yes, much faster
- No — they are a compile-time-only feature
- Only with bounds
- Only for collections
Answer: No — they are a compile-time-only feature. Generics are purely compile-time; after erasure runtime cost is unchanged.
For a type parameter bounded by an interface, which keyword do you use?
- implements
- super
- extends
- with
Answer: extends. For both classes and interfaces in a bound you write 'extends', e.g. <T extends Comparable<T>>.