Generics
Generics let you write classes and methods that work with any type while keeping full type safety. List<String> is a list that can only contain strings — the compiler checks every add and you never need a cast when reading. Understanding generics is essential for using the collections framework and writing reusable code.
Why generics?
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("Ana"); names.add("Ben"); String first = names.get(0); System.out.println(first.toUpperCase()); } }
ANA
The type argument in angle brackets says what the list holds. Adding a number would be a compile error, and get returns a String directly. Before generics (Java 5), collections held Object, required casts, and type mistakes only showed up at run time. The empty <> on the right — the "diamond" — lets the compiler infer the type.
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add(42); } }
Main.java:7: error: incompatible types: int cannot be converted to String
Writing a generic class
class Box<T> { private T content; void put(T item) { content = item; } T get() { return content; } boolean isEmpty() { return content == null; } } public class Main { public static void main(String[] args) { Box<String> letter = new Box<>(); letter.put("Hello"); Box<Integer> number = new Box<>(); number.put(7); System.out.println(letter.get().length() + number.get()); } }
12
T is a type parameter — a placeholder filled in when the class is used. By convention, type parameters are single capital letters: T (type), E (element), K/V (key/value).
Several type parameters
record Pair<A, B>(A first, B second) { <C> Pair<A, C> withSecond(C newSecond) { return new Pair<>(first, newSecond); } } public class Main { public static void main(String[] args) { Pair<String, Integer> score = new Pair<>("Ana", 91); Pair<String, Boolean> passed = score.withSecond(score.second() >= 50); System.out.println(score + " " + passed); } }
Pair[first=Ana, second=91] Pair[first=Ana, second=true]
Generic methods
import java.util.List; public class Main { static <T> T lastOf(List<T> items) { return items.get(items.size() - 1); } 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; } public static void main(String[] args) { System.out.println(lastOf(List.of("a", "b", "c"))); System.out.println(largest(List.of(4, 19, 7))); System.out.println(largest(List.of("pear", "apple", "zucchini"))); } }
c 19 zucchini
A method can declare its own type parameter before the return type. T extends Comparable<T> is a bound: T can be any type that can be compared with itself, which lets the method call compareTo.
Wildcards
import java.util.ArrayList; import java.util.List; public class Main { static double sum(List<? extends Number> numbers) { double total = 0; for (Number n : numbers) total += n.doubleValue(); return total; } static void addDefaults(List<? super Integer> target) { target.add(0); target.add(100); } public static void main(String[] args) { System.out.println(sum(List.of(1, 2, 3))); System.out.println(sum(List.of(1.5, 2.5))); List<Number> mixed = new ArrayList<>(); addDefaults(mixed); System.out.println(mixed); } }
6.0 4.0 [0, 100]
A List<Integer> is not a List<Number> (otherwise you could add a Double to a list of integers). Wildcards loosen this: ? extends Number accepts lists of any number type for reading; ? super Integer accepts lists that can receive integers. Mnemonic: "producer extends, consumer super".
Type erasure
Generic type information is checked at compile time and then mostly erased: at run time a List<String> and a List<Integer> are both just List. That's why you can't write new T(), create generic arrays like new T[10], or use primitives as type arguments.
Try it yourself
Write a generic method that counts how many items in a list match a value.
3 3