Generics (<T>)
A mechanism for defining classes and methods that work with any type by using type parameters. Collection classes (such as List<T>) are prime examples, and generics guarantee type safety at compile time (Java 5 and later).
Syntax
// Defines a generic class.
class Box<T> {
private T value;
Box(T value) { this.value = value; }
T get() { return value; }
}
// Defines a generic method.
static <T> T firstElement(List<T> list) {
return list.get(0);
}
// Sets an upper bound on the type parameter (T must be a subtype of Number).
class NumericBox<T extends Number> {
T value;
double doubleValue() { return value.doubleValue(); }
}
// Wildcard: represents an unknown type.
void printList(List<?> list) { /* accepts a List of any type */ }
// Upper-bounded wildcard: accepts subtypes of Number.
double sumList(List<? extends Number> list) {
return list.stream().mapToDouble(Number::doubleValue).sum();
}
// Lower-bounded wildcard: accepts supertypes of Integer.
void addNumbers(List<? super Integer> list) {
list.add(42);
}
Common Notation
| Notation | Description |
|---|---|
| <T> | Type parameter. Represents an arbitrary type. By convention, T (Type), E (Element), K (Key), and V (Value) are commonly used. |
| <T extends Number> | Sets an upper bound on the type parameter. T is restricted to Number or its subclasses. |
| <?> | Wildcard. Represents an unknown type. Used for read-only operations. |
| <? extends T> | Upper-bounded wildcard. Accepts subtypes of T (used for output/reading). |
| <? super T> | Lower-bounded wildcard. Accepts supertypes of T (used for input/writing). |
Sample Code
import java.util.*;
// Uses the generic class.
Box<String> strBox = new Box<>("Hello");
Box<Integer> intBox = new Box<>(42);
System.out.println(strBox.get()); // Prints "Hello".
System.out.println(intBox.get()); // Prints "42".
// Uses the generic method.
List<String> names = List.of("Alice", "Bob", "Carol");
System.out.println(firstElement(names)); // Prints "Alice".
// Uses a type bound to restrict to subclasses of Number.
NumericBox<Integer> nb = new NumericBox<>();
nb.value = 100;
System.out.println(nb.doubleValue()); // Prints "100.0".
// Uses wildcards to process lists of multiple types.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
List<Double> dbls = List.of(1.5, 2.5, 3.5);
System.out.println(sumList(ints)); // Prints "15.0".
System.out.println(sumList(dbls)); // Prints "7.5".
Notes
Using generics lets you write type-safe code without casting to Object. Since only String values can be added to a List<String>, runtime ClassCastException errors are prevented.
Java generics are implemented through a mechanism called type erasure: type information is removed after compilation and replaced with Object. As a result, you cannot check a generic type parameter with instanceof at runtime, nor create an instance with new T().
For combining lambda expressions with generics, see Predicate / Function / Consumer / Supplier. For working with collection classes, refer to the corresponding pages.
If you find any errors or copyright issues, please contact us.