Java泛型super/extends行为差异(OCP Java 8备考)
extends vs super in Java Generics (OCP Java 8 Focus) Hey there! Since you're prepping for OCP Java 8 and already have a solid grasp on List<? super A> behavior, let's dive into how the extends wildcard differs—using your class hierarchy as a reference to make things concrete.
First, let's recap your class structure for clarity:
java.lang.Object class A extends Object { public boolean mA() {return true;} } class B extends A { public boolean mB() {return false;} }
1. What List<? extends A> accepts as arguments
Unlike List<? super A> (which takes ArrayList<A> or ArrayList<Object>), List<? extends A> can only work with lists of A or its subclasses. That means valid arguments include:
new ArrayList<A>()new ArrayList<B>()- Any other list where the generic type is a subclass of A
You cannot pass an ArrayList<Object> here—since Object is a superclass of A, not a subclass, it violates the extends constraint.
2. Behavior of get() with extends
When you call get() on a List<? extends A>, the compiler guarantees the returned object is an instance of A (or a subclass of A). This is safe because every subclass of A inherits from A, so you can safely call methods defined in A on the returned item.
Example:
public static void m2(List<? extends A> l) { A retrievedItem = l.get(0); // Compiles perfectly retrievedItem.mA(); // Works—mA() is defined in A // retrievedItem.mB(); ❌ Compile error! We can't guarantee the item is a B }
Compare this to your List<? super A> example, where get() only returns Object—you were limited to calling Object methods like toString().
3. Behavior of add() with extends
This is where the biggest contrast with super comes in: you cannot add any non-null elements to a List<? extends A>.
Why? The compiler doesn't know the exact type of the list. For example:
- If the list is actually a
List<B>, adding anAinstance would be invalid (since A is a superclass of B, not a subclass). - Even adding a
Binstance isn't allowed—because the list could be a plainList<A>, but the compiler errs on the side of safety to avoid runtime errors.
The only exception is null, since null is a valid value for any reference type:
public static void m2(List<? extends A> l) { l.add(new A()); // ❌ Compile error l.add(new B()); // ❌ Compile error l.add(null); // ✅ Works—null is compatible with all types }
Again, contrast this with List<? super A>, where you can freely add A or B instances (since any superclass of A can hold A and its subclasses).
4. The PECS Rule (Key for OCP)
To remember the difference, use the PECS principle (Producer Extends, Consumer Super):
- Use
extendswhen your code is producing elements from the list (reading/getting items)—you're pulling items out, and you know they're at least of the upper bound type (A). - Use
superwhen your code is consuming elements into the list (writing/adding items)—you're putting items in, and you know the list can accept at least the lower bound type (A) and its subclasses.
That's the core of the behavior difference—this will be critical for your OCP exam questions on generics!
内容的提问来源于stack exchange,提问作者Ricardo Fonseca

