Generics and Wildcards in Java

Master generics, type parameters, bounded types, wildcards (? extends, ? super), generic methods, type erasure, and raw types for the OCP 21 exam.

Table of Contents

1. Generics Overview

Generics enable types (classes and interfaces) to be parameters when defining classes, interfaces, and methods. They provide type safety and eliminate the need for casting.

1.1 Benefits of Generics

  • Type Safety: Compile-time type checking
  • Eliminates Casting: No need for explicit casts
  • Code Reuse: Write generic code that works with different types

2. Type Parameters

2.1 Generic Classes

Example:
// Generic class
class Box<T> {
    private T value;
    
    public void setValue(T value) {
        this.value = value;
    }
    
    public T getValue() {
        return value;
    }
}

// Using generic class
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String value = stringBox.getValue();  // No casting needed

Box<Integer> intBox = new Box<>();
intBox.setValue(42);
Integer num = intBox.getValue();

2.2 Multiple Type Parameters

Example:
// Multiple type parameters
class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> pair = new Pair<>("age", 30);
String key = pair.getKey();
Integer value = pair.getValue();

3. Bounded Types

3.1 Upper Bounded Wildcards

Example:
// Upper bound - T must be Number or subclass
class NumberBox<T extends Number> {
    private T value;
    
    public double getDoubleValue() {
        return value.doubleValue();  // Can call Number methods
    }
}

NumberBox<Integer> intBox = new NumberBox<>();
NumberBox<Double> doubleBox = new NumberBox<>();
// NumberBox<String> stringBox = new NumberBox<>();  // Compilation error

3.2 Multiple Bounds

Example:
// Multiple bounds
class Example<T extends Number & Comparable<T>> {
    // T must extend Number AND implement Comparable
}

// First bound must be class, then interfaces
// class Example<T extends Comparable<T> & Number> { }  // Error

4. Wildcards

4.1 Unbounded Wildcards (?)

Example:
// Unbounded wildcard - accepts any type
public void processList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
    // Cannot add elements (except null)
    // list.add("item");  // Compilation error
}

List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
processList(stringList);  // OK
processList(intList);     // OK

4.2 Upper Bounded Wildcards (? extends)

Example:
// Upper bounded wildcard - Number or subclass
public double sum(List<? extends Number> numbers) {
    double sum = 0.0;
    for (Number num : numbers) {
        sum += num.doubleValue();
    }
    return sum;
    // Cannot add elements (except null)
    // numbers.add(10);  // Compilation error
}

List<Integer> ints = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.0, 2.0, 3.0);
sum(ints);     // OK
sum(doubles);  // OK

4.3 Lower Bounded Wildcards (? super)

Example:
// Lower bounded wildcard - Integer or superclass
public void addNumbers(List<? super Integer> list) {
    list.add(1);      // Can add Integer
    list.add(2);
    // Can read as Object
    Object obj = list.get(0);
}

List<Number> numbers = new ArrayList<>();
List<Object> objects = new ArrayList<>();
addNumbers(numbers);  // OK
addNumbers(objects);  // OK

5. Generic Methods

5.1 Defining Generic Methods

Example:
// Generic method
public static <T> T getFirst(List<T> list) {
    return list.get(0);
}

// Generic method with bounds
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

// Using generic methods
String first = getFirst(Arrays.asList("a", "b", "c"));
Integer max = max(10, 20);

// Type inference
String inferred = getFirst(Arrays.asList("a", "b"));  // Type inferred

6. Type Erasure

Type erasure is the process by which the compiler removes all type parameters and replaces them with their bounds or Object.

6.1 How Type Erasure Works

Example:
// Generic code
class Box<T> {
    private T value;
    public void setValue(T value) { this.value = value; }
    public T getValue() { return value; }
}

// After type erasure (conceptually)
class Box {
    private Object value;
    public void setValue(Object value) { this.value = value; }
    public Object getValue() { return value; }
}

// Raw types (avoid using)
Box rawBox = new Box();  // Raw type - no type parameter
rawBox.setValue("String");
Object value = rawBox.getValue();  // Returns Object

6.2 Type Erasure Implications

  • Cannot use instanceof with generic types
  • Cannot create arrays of parameterized types
  • Cannot catch generic exceptions
  • Overloading with same erasure is not allowed

7. Exam Key Points

Critical Concepts for OCP 21 Exam:

  • Generics: Type parameters for classes, interfaces, methods
  • Type Parameter: T, E, K, V are common names
  • Diamond Operator: <> for type inference
  • Upper Bound: <T extends Type> - T must be Type or subclass
  • Lower Bound: <? super Type> - Type or superclass
  • Wildcard: ? represents unknown type
  • ? extends: Upper bounded wildcard, read-only
  • ? super: Lower bounded wildcard, write-only
  • Unbounded ?: Accepts any type, read-only
  • Generic Methods: Methods with type parameters
  • Type Erasure: Generics removed at runtime
  • Raw Types: Generic types without type parameters (avoid)
  • PECS: Producer Extends, Consumer Super

Post a Comment

0 Comments