In Java, assume I have the following class Container that contains a list of class Items:
public class Container<T>
{
private List<Item<? extends T>> items;
开发者_StackOverflow社区 private T value;
public Container(T value)
{
this.value = value;
}
public void addItem(Item<? extends T> item)
{
items.add(item);
}
public void doActions()
{
for (Item<? extends T> item : items)
{
item.doAction(value);
}
}
}
public abstract class Item<T>
{
public abstract void doAction(T item);
}
Eclipse gives the error:
The method doAction(capture#1-of ? extends T) in the type Item is not applicable for the arguments (T)
I've been reading generics examples and various postings around, but I still can't figure out why this isn't allowed. Eclipse also doesn't give any helpful tips in its proposed fix, either. The variable value
is of type T, why wouldn't it be applicable for ? extends T
?.
Take a look at the following program
public class Cell<T> {
private T value;
public void set(T t) { value = t; }
public T get() { return value; }
}
Cell<Integer> ci = new Cell<Integer>();
Cell<? extends Number> cn = ci;
cn.set(new Double(5.0)); // (A) <-- Compile error here
Integer n = ci.get(); // (B) Runtime error!
As you said, the (A) line does not compile. If this line were legal then, at runtime, the program will pass a Double
object to the cn.set()
call where cn
's dynamic type is Cell<Integer>
.
When execution subsequently arrives at (B), ci.get()
will return a Double
object---the one that was passed in (A)---were the declaration of ci
says that its get()
method is guaranteed to return an Integer
. In order to prevent this conundrum (which actually breaks the JVM's strong typing philosophy), the compiler disallows assignment from T
to <? extends T>
.
PECS - Producer-extends, consumer-super
replace all occurrences of extends
with super
in your declarations (including the one you omitted, in the loop), and it compiles :)
See my (only!) blog post. It doesn't directly talk about the "? extends T" case, but everywhere you see "?", just imagine it says "? extends Object" and it should become clear.
Basically, you've got a list of "something that extends T", but you don't know what that something is at compile time.
The bounding is reversed. Item<X> can take an X or a subclass of X for its doAction. Now ? extends T
means that X is a subclass of T. So you're passing in a T into an item that expects an X, which is a subclass of T, not the other way around.
精彩评论