COLLECTIONS & GENERICS
Generics: Bounded Wildcards (READ-WRITE Rule)
READ-WRITE Rule: Read Extends, Write Super
? extends T
: READ-ONLY - Can read items of type T or its subtypes. Cannot add anything (exceptnull
)? super T
: WRITE-ONLY - Can write T or its subtypes. Cannot safely read (except asObject
)
Basic READ-WRITE Example
// Read Extends - Reading from a collection
List numbers = List.of(1, 2.0, 3L);
Number n = numbers.get(0); // β
OK - can read as Number
// numbers.add(3); // β Compile error - cannot write
// Write Super - Writing to a collection
List values = new ArrayList();
values.add(10); // β
OK - can write Integer/subtypes
values.add(42); // β
OK - can write Integer/subtypes
// Integer i = values.get(0); // β Compile error - can only read as Object
Object obj = values.get(0); // β
OK - can read as Object
Family Hierarchy Example
class Grandparent {
}
class Parent extends Grandparent {
}
class Child extends Parent {
}
public class FamilyGenericTest {
Grandparent grandparent = new Grandparent();
Child child = new Child();
public void acceptOlderGenerations(List familyList) {
// Can WRITE Parent and younger generations (Child)
familyList.add(new Parent()); // β
OK - Parent is exactly Parent
familyList.add(child); // β
OK - Child is subtype of Parent
// familyList.add(grandparent); // β NO - Grandparent is not assignable to Parent
// Can only READ as Object (safest common type)
Object obj = familyList.get(0); // β
OK - everything is Object
// Parent p = familyList.get(0); // β NO - could be List
// Child c = familyList.get(0); // β NO - definitely wrong
}
public void acceptYoungerGenerations(List familyList) {
// Cannot WRITE anything except null
// familyList.add(grandparent); // β NO - cannot add to ? extends
// familyList.add(new Parent()); // β NO - cannot add to ? extends
// familyList.add(child); // β NO - cannot add to ? extends
familyList.add(null); // β
OK - null is allowed
// Can READ as Parent or higher in hierarchy
Parent p = familyList.get(0); // β
OK - could be Parent or Child
Grandparent gp = familyList.get(0); // β
OK - Parent extends Grandparent
// Child c = familyList.get(0); // β NO - could be just Parent, not Child
}
}
Why This Works
? super Parent
: List could beList<Parent>
orList<Grandparent>
- Safe to write: Parent/Child can go into either list type
- Unsafe to read: Donβt know if itβs Parent or Grandparent, so can only read as Object
? extends Parent
: List could beList<Parent>
orList<Child>
- Unsafe to write: Donβt know exact type, so canβt add anything safely
- Safe to read: Whatever comes out is at least a Parent
π‘ Learning Tip:
? super T
= βI can WRITE T and its children to thisβ (write T, read Object)? extends T
= βI can READ T and its children from thisβ (read T, write nothing)
Q: In List<? super Parent> family
, why canβt you read the result as Parent
?
A: Because the list could actually be List<Grandparent>
, and reading a Grandparent as Parent would be unsafe downcasting.