Java Generics - Complete Tutorial
Master Java Generics: Learn type parameters, generic classes and methods, bounded types, wildcards, type erasure with practical examples and best practices for type-safe code.
1. Introduction to Generics
Generics let you parameterize types so collections and classes enforce type safety at compile time without casting.
- Type parameters: List
- Compile-time type checking
- Eliminates unsafe casts
- Introduced in Java 5
import java.util.*;
public class GenericsIntro {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Java");
String first = names.get(0);
System.out.println(first);
}
}
2. Generic Classes and Methods
Define your own generic types with type parameters in angle brackets. Generic methods declare type params before the return type.
- class Box
{ T value; } - Multiple params: Map
- Generic methods:
void print(T item) - Cannot use primitives as type args
class Pair<K, V> {
K key;
V value;
Pair(K key, V value) { this.key = key; this.value = value; }
}
public class GenericClassDemo {
public static void main(String[] args) {
Pair<String, Integer> p = new Pair<>("age", 25);
System.out.println(p.key + " " + p.value);
}
}
3. Bounded Type Parameters
Bounds restrict type parameters to subclasses of a class or implementors of an interface using extends.
limits to Number subclasses - Multiple bounds: T extends A & B
- Wildcards: ? extends Number
- ? super Integer for lower bound
class NumberBox<T extends Number> {
T value;
NumberBox(T value) { this.value = value; }
double doubleValue() { return value.doubleValue(); }
}
4. Type Erasure
Java removes generic type information at compile time for backward compatibility. At runtime, List
- Generics are compile-time feature
- Runtime cannot use new T()
- instanceof with generics limited
- Bridge methods preserve polymorphism
import java.util.*;
public class ErasureDemo {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<Integer> ints = new ArrayList<>();
System.out.println(strings.getClass() == ints.getClass());
}
}
5. Practical Generics Examples
Generics appear in repositories, API responses, and reusable data structures throughout enterprise Java.
- Generic repository interface
- Response wrapper Response
- Utility method Collections.max with Comparable
- EnumMap and EnumSet type-safe enums
public class GenericUtil {
static <T> void swap(T[] arr, int i, int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
6. Generics Best Practices
Use generics everywhere instead of raw types. Prefer bounded wildcards for flexible APIs.
- Never use raw types in new code
- PECS: Producer extends, Consumer super
- Avoid generic arrays creation
- Use @SuppressWarnings sparingly
- Name type params meaningfully: T, E, K, V