2010-06-13 2 views
1

В Java, предположим, что у меня есть следующий класс контейнер, который содержит список классов предметов:Почему я не могу передать объект типа T методу на объект типа <? extends T>?

 

    public class Container<T> 
    { 
     private List<Item<? extends T>> items; 

     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, дает ошибку: The method doAction(capture#1-of ? extends T) in the type Item is not applicable for the arguments (T)

Я читала дженерики примеры и различные проводки вокруг, но я до сих пор не могу понять, почему это запрещено. Eclipse также не дает никаких полезных советов в предлагаемом исправлении. Переменная value относится к типу T, почему бы не применимо к ? extends T ?.

ответ

7

Взгляните на следующую программу

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! 

Как вы сказали, линия (А) не компилируется. Если эта строка была законной, то во время выполнения программа передает объект Double на вызов cn.set(), где динамический тип cn - Cell<Integer>.

Когда выполнение впоследствии прибывает в (В), ci.get() возвращает объект Double --- тот, который был принят в (А) --- были декларация ci говорит, что его метод get() гарантированно возвращает Integer , Чтобы предотвратить эту загадку (которая фактически нарушает сильную философию типизации JVM), компилятор запрещает присвоение от T до <? extends T>.

+0

Это имеет смысл. Я не рассматривал такое промежуточное задание. –

1

См. my (only!) blog post. Он напрямую не говорит о случае «? Extends T», но везде, где вы видите «?», Просто представьте, что он говорит: «Расширяет объект», и он должен стать ясным.

В принципе, у вас есть список «что-то, что расширяет T», но вы не знаете, что это такое во время компиляции.

2

PECS - Producer-extends, consumer-super

заменить все вхождения extends с super в ваших декларациях (в том числе и тот, который вы опущены, в цикле), и он компилирует :)

+0

Я действительно пробовал это, но это не дает мне то, что я хотел. Однако это может быть лучшее, на что я могу надеяться. –

+0

что он не дал вам? – Bozho

+0

Я действительно хотел иметь возможность добавить Item в контейнер, где S является подклассом T. С супер я могу добавить, например, Item . –

0

Ограничивающими восстанавливаются. Пункт < X> может принимать X или подкласс X для его doAction. Теперь ? extends T означает, что X является подклассом T. Таким образом, вы передаете T в элемент, который ожидает X, который является подклассом T, а не наоборот.

Смежные вопросы