Understanding Generics Advanced Concepts

1. Creating Generic Classes

FormSyntax
Single paramclass Box<T> { T value; }
Multipleclass Pair<K, V> { ... }
Diamondnew HashMap<>() infers
StaticCannot use class type params; use method generics

Example: Generic container

public final class Cache<K, V> {
    private final Map<K, V> map = new ConcurrentHashMap<>();
    public V get(K key, Function<K, V> loader) { return map.computeIfAbsent(key, loader); }
}

2. Creating Generic Methods

PositionSyntax
Type paramBefore return type: <T> T pick(...)
StaticCan declare own params independent of class
InferenceCompiler infers from arguments
WitnessPass Class<T> when erased

3. Using Bounded Type Parameters (<T extends>)

BoundMeaning
<T extends Number>T is Number or subtype
<T extends Comparable<T>>Self-referential bound
Multiple<T extends A & B> (class first)

4. Using Wildcards (?, extends, super)

WildcardMeaningRead/Write
List<?>Unknown typeRead as Object only
List<? extends Number>Producer (covariant)Read Number, no add
List<? super Integer>Consumer (contravariant)Add Integer, read Object

5. Understanding PECS Principle

MnemonicRuleExample
Producer ExtendsSource data → use extendscopy(? extends T src, ...)
Consumer SuperDestination → use supercopy(..., ? super T dst)
BothInvariant TRead+write same type

Example: PECS in Collections.copy

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (T t : src) dest.add(t);
}

6. Using Multiple Bounds (<T extends A & B>)

RuleDetail
OrderClass (if any) first, then interfaces
LimitAt most one class bound
CompilerErases to first bound

Example: Number + Comparable

public static <T extends Number & Comparable<T>> T max(List<T> xs) {
    return xs.stream().max(Comparator.naturalOrder()).orElseThrow();
}

7. Understanding Type Erasure

What's ErasedWhat Remains
Type params at runtimeRaw type + casts
Bounded → first bounde.g., <T extends Number> → Number
Bridge methodsSynthetic for covariant returns
SignaturesStored in metadata for reflection
Warning: Cannot do new T(), x instanceof List<String>, or new T[] due to erasure.

8. Using Recursive Type Bounds (<T extends Comparable<T>>)

PatternUse
<T extends Comparable<T>>Self-comparable types
<T extends Enum<T>>Enum subtypes
Builderclass Builder<T extends Builder<T>> for fluent self-type

9. Handling Generic Arrays

IssueWorkaround
new T[n]Forbidden — use (T[]) Array.newInstance(cls, n)
new List<String>[10]Forbidden — use List<List<String>>
VarargsHeap pollution; mark with @SafeVarargs
Cast warningSuppress with @SuppressWarnings("unchecked")

10. Using Raw Types

FormEffect
List listDisables generic type checks
InteropPre-1.5 code only
CompilerUnchecked warnings
AvoidUse List<?> instead

11. Understanding Reification vs Erasure

ReifiedErased
Arrays (know component type)Generic types (no T at runtime)
PrimitivesType variables
Class<?> rawClass<String> param info lost

12. Working with Type Tokens (Class<T>)

TokenUse
Class<T>Pass concrete class for runtime type
TypeReference (Jackson)Capture parameterized types
Super-type tokenAnonymous subclass to retain generic info

Example: Super-type token

abstract class TypeRef<T> {
    public final Type type = ((ParameterizedType) getClass().getGenericSuperclass())
        .getActualTypeArguments()[0];
}
TypeRef<List<String>> ref = new TypeRef<>() {};