Generics
Generics let you write classes and methods that work over a type parameter while keeping compile-time safety. The type information is erased at runtime (no List
Generic methods, bounded types, and wildcards
EXAMPLE
import java.util.*;
import java.util.function.*;
public class GenericsDemo {
// 1) Generic method
public static <T> List<T> listOf(T... values) {
var out = new ArrayList<T>(values.length);
Collections.addAll(out, values);
return out;
}
// 2) Bounded type parameter: T must implement Comparable<T>
public static <T extends Comparable<T>> T max(List<T> xs) {
if (xs.isEmpty()) throw new NoSuchElementException();
T best = xs.get(0);
for (int i = 1; i < xs.size(); i++) {
if (xs.get(i).compareTo(best) > 0) best = xs.get(i);
}
return best;
}
// 3) Generic class with two type parameters
public static class Pair<A, B> {
final A first; final B second;
public Pair(A a, B b) { this.first = a; this.second = b; }
public <C> Pair<A, C> withSecond(C c) { return new Pair<>(first, c); }
@Override public String toString() { return "(" + first + ", " + second + ")"; }
}
// 4) Wildcards
// ? extends T -> covariant: producer of T
// ? super T -> contravariant: consumer of T
public static double sumOf(List<? extends Number> xs) {
double s = 0;
for (Number n : xs) s += n.doubleValue(); // safe to READ as Number
return s;
}
public static <T> void addAll(List<? super T> sink, Iterable<T> src) {
for (T t : src) sink.add(t); // safe to WRITE T into sink
}
// 5) Type-safe builder using a functional interface
public static <T> List<T> build(Consumer<List<T>> body) {
var list = new ArrayList<T>();
body.accept(list);
return list;
}
public static void main(String[] args) {
var ints = listOf(1, 2, 3);
System.out.println(max(ints)); // 3
System.out.println(sumOf(ints)); // 6.0
Pair<String, Integer> p1 = new Pair<>("life", 42);
Pair<String, Double> p2 = p1.withSecond(3.14);
System.out.println(p2); // (life, 3.14)
List<Number> nums = new ArrayList<>();
addAll(nums, ints); // OK: List<Number> super List<Integer>
System.out.println(nums);
List<String> built = build(l -> { l.add("a"); l.add("b"); });
System.out.println(built);
}
}
Why it matters
The PECS mnemonic — Producer Extends, Consumer Super — is the rule of thumb for wildcards. A method that reads from a collection takes `? extends T`; a method that writes into a collection takes `? super T`. Get this right and your APIs accept the most useful types without sacrificing safety.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
class Box<T> {
T item;
public void set(T item) { this.item = item; }
public T get() { return item; }
}
Box<Integer> b = new Box<>();
b.set(42);
Try it Yourself »
Discussion
Loading…