Working with Generics

1. Creating Generic Classes

Example: Generic class

public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}
Box<String> b = new Box<>();   // diamond operator
ConventionMeaning
TType
EElement (collections)
K, VKey, Value (maps)
RReturn
NNumber

2. Creating Generic Methods

Example: Generic method

public static <T> List<T> repeat(T item, int n) {
    List<T> list = new ArrayList<>();
    for (int i = 0; i < n; i++) list.add(item);
    return list;
}
List<String> xs = repeat("x", 3);
List<Integer> ones = ListUtil.<Integer>repeat(1, 5);   // explicit type witness
ElementDetail
Type parameterDeclared before return type
InferenceUsually from arguments

3. Using Bounded Type Parameters

FormMeaning
<T extends Number>T is Number or subtype
<T extends A & B>Multiple bounds (one class max, then interfaces)
<T extends Comparable<T>>Recursive bound

4. Understanding Wildcards (?, extends, super)

WildcardMeaning
<?>Unknown type — read-only as Object
<? extends T>T or subtype — covariant (read)
<? super T>T or supertype — contravariant (write)

5. Using Upper Bounded Wildcards (? extends T)

Example: Producer of T

double sum(List<? extends Number> list) {
    double s = 0;
    for (Number n : list) s += n.doubleValue();
    return s;
}
sum(List.of(1, 2, 3));        // List<Integer> OK
sum(List.of(1.5, 2.5));       // List<Double> OK
OperationAllowed?
Read as TYes
Add TNo (except null)

6. Using Lower Bounded Wildcards (? super T)

Example: Consumer of T

void addNumbers(List<? super Integer> list) {
    for (int i = 0; i < 5; i++) list.add(i);
}
addNumbers(new ArrayList<Number>());   // OK
addNumbers(new ArrayList<Object>());   // OK
OperationAllowed?
Add T or subtypeYes
ReadOnly as Object

7. Understanding Unbounded Wildcards (?)

UseDetail
Type-agnostic opslist.size(), list.clear()
Print/inspectWhen type doesn't matter

8. Understanding Type Erasure

AspectDetail
CompileGeneric types checked
RuntimeErased to bound (default Object)
Bridge methodsCompiler-generated for polymorphism
RestrictionsNo new T(), no T.class, no instanceof T

9. Working with Generic Arrays Limitations

OperationAllowed?
new T[10]No (compile error)
new List<String>[10]No (generic array creation)
(T[]) new Object[n]Yes (with unchecked warning)
WorkaroundUse ArrayList<T> or Array.newInstance

10. Using Multiple Type Parameters

Example: Pair

public record Pair<K, V>(K key, V value) {}
Pair<String, Integer> p = new Pair<>("age", 30);
AspectDetail
Comma-separated<K, V>
InferenceDiamond operator <>

11. Understanding PECS Principle (Producer Extends Consumer Super)

RoleWildcardExample
Producer (read T)? extends Tcopy(src: ? extends T, ...)
Consumer (write T)? super Tcopy(..., dst: ? super T)
BothUse exact T

12. Understanding Generic Method Inference

SourceInference
Argument typesCollections.emptyList() uses target type
Target typeVariable, return type
Explicit witnessClass.<Type>method()