Generics let you write a class or method once and use it with any type while keeping compile-time type checks. List<String> only accepts Strings — no casts, and mistakes are caught by the compiler.
Declare type parameters on a class (class Box<T>) or a method (static <T> T firstOrDefault(List<T> list, T fallback)). Bound them with extends when you need certain capabilities: <T extends Comparable<T>> can be compared.
Wildcards make APIs flexible: List<? extends Number> reads numbers of any subtype; List<? super Integer> accepts Integers being added. A handy rule: producer extends, consumer super (PECS).
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class GenericsDemo {
static class Repository<T, ID> {
private final List<T> items = new ArrayList<>();
private final java.util.function.Function<T, ID> idOf;
Repository(java.util.function.Function<T, ID> idOf) { this.idOf = idOf; }
void save(T item) { items.add(item); }
Optional<T> findById(ID id) {
return items.stream().filter(i -> idOf.apply(i).equals(id)).findFirst();
}
List<T> findAll() { return List.copyOf(items); }
}
record Pair<A, B>(A first, B second) {}
record Student(String id, String name, int score) {}
static <T extends Comparable<T>> T max(List<T> values) {
T best = values.get(0);
for (T v : values) if (v.compareTo(best) > 0) best = v;
return best;
}
static double sum(List<? extends Number> numbers) { // reads Integers, Doubles, ...
double total = 0;
for (Number n : numbers) total += n.doubleValue();
return total;
}
static void addDefaults(List<? super Integer> target) { // accepts List<Integer>, List<Number>, ...
target.add(0);
target.add(100);
}
public static void main(String[] args) {
Repository<Student, String> repo = new Repository<>(Student::id);
repo.save(new Student("S1", "Amina", 88));
repo.save(new Student("S2", "Juma", 42));
System.out.println(repo.findById("S2").map(Student::name).orElse("not found"));
System.out.println(repo.findById("S9").map(Student::name).orElse("not found"));
Pair<String, Integer> top = new Pair<>("Amina", 88);
System.out.println(top.first() + " -> " + top.second());
System.out.println(max(List.of(3, 9, 4)) + " " + max(List.of("Juma", "Amina", "Neema")));
System.out.println(sum(List.of(1, 2, 3)) + sum(List.of(0.5, 1.5)));
List<Number> numbers = new ArrayList<>();
addDefaults(numbers);
System.out.println(numbers);
}
}Key points
- Generics give reusable code with compile-time type safety — no casts.
- Bound type parameters (
<T extends Comparable<T>>) to use their capabilities. - Wildcards:
? extends Tto read (producer),? super Tto write (consumer).
Exercise
Write a generic Stack<T> class with push, pop, peek and isEmpty (throw an exception on empty pop). Then write a generic method <T> Map<T, Integer> frequencies(List<T> items).
Show solution
Try the exercise yourself first — then compare your approach with this one.
Stack<T> wraps an ArrayDeque<T>, so pushes and pops are fast and type-safe. pop on an empty stack throws a clear exception. frequencies works for any element type because it's a generic method.
import java.util.*;
public class GenericStack {
static class Stack<T> {
private final Deque<T> items = new ArrayDeque<>();
void push(T item) { items.push(item); }
T pop() {
if (items.isEmpty()) throw new NoSuchElementException("pop from an empty stack");
return items.pop();
}
T peek() { return items.peek(); }
boolean isEmpty() { return items.isEmpty(); }
}
static <T> Map<T, Integer> frequencies(List<T> items) {
Map<T, Integer> counts = new LinkedHashMap<>();
for (T item : items) counts.merge(item, 1, Integer::sum);
return counts;
}
public static void main(String[] args) {
Stack<String> pages = new Stack<>();
pages.push("Home");
pages.push("Lessons");
System.out.println(pages.pop() + " then " + pages.peek()); // Lessons then Home
pages.pop();
try {
pages.pop();
} catch (NoSuchElementException e) {
System.out.println("Error: " + e.getMessage());
}
System.out.println(frequencies(List.of("A", "B", "A", "C", "A"))); // {A=3, B=1, C=1}
System.out.println(frequencies(List.of(3, 1, 3))); // {3=2, 1=1}
}
}