Java泛型:List<? extends Shape>与List<Shape>的编译疑问
List<? extends Shape> Assignment Works but addAll Doesn't? Great question—this is one of those tricky Java generics scenarios that trips up even experienced devs! Let's break down exactly why these two lines behave differently.
First, let's recap the types we're dealing with:
List<? extends Shape> typeList: This is an upper-bounded wildcard list. It means "this is a list of some subclass ofShape(includingShapeitself), but we don't know which specific subclass."List<Shape> shapeList: This is a concrete list that holdsShapeinstances (and any of its subclasses, since Java allows upcasting).
Why Line 2 (typeList = shapeList) Compiles Successfully
Java's generics rules allow assigning a more specific list type to a wildcard-based list type here. Think of it like this:List<Shape> is a valid implementation of List<? extends Shape> because every element in shapeList is guaranteed to be a Shape (or subclass), which fits the wildcard's "any Shape subclass (including Shape)" requirement.
This assignment is safe because when you have a List<? extends Shape>, you're only allowed to read elements from it (you can't add anything except null to it). Since we're not modifying the list through typeList, there's no risk of violating type safety.
Why Line 1 (typeList.addAll(shapeList)) Fails to Compile
Let's look at the addAll method signature for a List<E>:
boolean addAll(Collection<? extends E> c)
When we call this on typeList (which is List<? extends Shape>), the compiler can't resolve what E actually is. Here's the problem:
Suppose typeList was actually pointing to a List<Circle> (where Circle is a subclass of Shape). In that case, addAll should only accept collections of Circle (or its subclasses). But shapeList is a List<Shape>—it could contain Square instances (another Shape subclass), which would break the List<Circle>'s type safety if added.
The compiler has no way to verify that shapeList's elements are compatible with the actual runtime type of typeList, so it blocks the call entirely to prevent potential type errors.
To make this concrete, imagine this code:
class Shape {} class Circle extends Shape {} class Square extends Shape {} List<Circle> circleList = new ArrayList<>(); List<? extends Shape> typeList = circleList; List<Shape> shapeList = new ArrayList<>(); shapeList.add(new Square()); // If this were allowed, circleList would now contain a Square—total type violation! // typeList.addAll(shapeList);
Key Takeaway
- Assignment: Assigning a concrete list to a wildcard list is safe because you're only restricting how you can interact with the list (read-only, essentially).
- Method calls that modify the list: Any operation that adds elements to a wildcard list (like
addAll) is blocked because the compiler can't guarantee the elements being added match the list's actual runtime type.
内容的提问来源于stack exchange,提问作者Shamran Siddique

