@DavidJoiner / все:
FWIW: новая версия Руководство по проектированию Алгоритма должна любой день теперь.
весь курс, для которого он профессор Skiena разработал эту книгу, также имеется в сети:
http://www.cs.sunysb.edu/~algorith/video-lectures/2007-1.html
Возможно, это поможет вам понять:
ArrayList<Object> aList = new ArrayList<String>();
Это не компилируется с аналогичной ошибкой.
EDIT: Проконсультируйся с: http://www.ibm.com/developerworks/java/library/j-jtp01255.html
Это в основном тот же случай, что и ранее. По сути, в случае универсальных шаблонов вам никогда не разрешается выполнять это задание:
Подумайте об этом примере:
ArrayList<Object> alist = new ArrayList<Number>();
Это не компилируется, потому что это небезопасно. Вы могли бы добавить Strings aList. Вы пытаетесь назначить список объектов, которые гарантированно будут числами, но могут быть любым числом, списку, который гарантирует только то, что вы будете содержать объекты, но это могут быть любые объекты. Если бы компилятор разрешил этот случай, он бы снял ограничение на то, какие типы объектов могут попадать в список. Вот почему вы должны использовать подстановочный знак?, Как таковой:
ArrayList<? extends Object> alist = new ArrayList<Number>();
В компилятор ArrayList extends Object>
, означает "ArrayList определенного типа '?' я не знаю, но знаю, что расширяет Object. Этот список ArrayList гарантированно будет содержать только элементы этого неизвестного символа '?' type, и поэтому содержит только объекты ". Однако в этом случае компилятор не позволит вам выполнить alist.add (2). Почему это так, поскольку компилятор не знает тип элементов списка, и не можете гарантировать, что вам разрешено вставлять в него объекты Integer.
Вы правы, думая, что D extends Object>
является супертипом D extends C>
. Однако List
не является подтипом List
, вы должны использовать List extends D extends C >>
.
Ваш случай в основном эквивалентен
ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>();
У вас та же проблема, что и выше,
Проверить тип объекта, возвращаемого вызовом Arrays.asList. Я бы предположил , что он возвращает List
extends C>
определяет верхние границы типа, что означает, что тип должен быть расширен от C. extends Object>
, по-видимому, является более общим, поэтому он несовместим.
Подумайте об этом варианте использования. Указав верхнюю границу, вы ожидаете, что будет реализован определенный минимальный интерфейс.
Это еще хуже. Вы даже не можете этого сделать:
List<D<?>> lDQ = Arrays.asList(new D<A>());
Это станет более понятным, если вы измените свою программу следующим образом:
List<D<? extends C>> a = Arrays.asList(new D<A>(), new D<B>()); //compiles
List<D<? extends Object>> b = a; //error
По сути, вы объявляете b
как нечто, что может принимать общее содержимое ( D <
something >
), но список, который вы ему назначаете, принимает только более конкретное содержимое ( D <
ближайший общий суперкласс A и B >
).
Объявление b
как List
подразумевает, что вы можете, скажем, b.add (new D
, но на самом деле это List
, поэтому вы не можете.
Нет.
extends C> - это не то же самое, что расширяет объект>
Если бы это было так, это привело бы также к (неустановленному) предположению - C расширяет объект, и привело бы к тому, что ...
class C {public void doSomethingMEaningful (); };
Список расширяет C> allAtSea = new ArrayList <...> (); Список расширяет Object> allObjects = new ArrayList <...> (); allObjects.add (новое целое число (88)); ...
allAtSea.addAll (allObjects); ...
allAtSea.get (...). DoSomethingMeaningful (...); // Ой-ой .. это находит целое число 88
FAQ по C ++ предоставляет наглядный пример для этого в 21.3
Вы не можете наследовать в параметре generics. Предположим, у вас есть следующие ссылки:
List<Object> a;
List<String> b;
Теперь вы назначаете обоим один и тот же список: a = b;
Если какой-то код выполняет следующее:
a.add(new Integer(1));
Что произойдет, если кто-то сделает следующее:
String s = b.get(0);
Вы получите целое число списка строк. Это не должно работать. Вот почему List и List несовместимы, хотя объектной ссылке можно присвоить String. Дженерики не работают с наследованием.
Я думаю, что это станет более понятным, если мы расширим ваш пример. Давайте добавим некоторые функции в E и подробно остановимся на первой строке вашего основного метода:
public static class E<T>{
private final T thing;
public void setThing(T thing) {
this.thing = thing;
}
public T getThing() {
return thing;
}
}
public static void main(String[] args) {
E<D<? extends C>> a1;
E<D<A>> a2 = new E<D<A>>();
a1 = a2; // this won't compile, but why?
// these things are permissible on a1:
a1.setThing(new D<A>());
a2.setThing(new D<B>());
// now let's try doing the same thing to a2:
a2.setThing(new D<A>());
a2.setThing(new D<B>()); // oops
}
Последняя строка нового основного метода объясняет, почему a1 не может быть установлено в a2. Если компилятор позволяет вам это сделать, то вы сможете поместить D в контейнер, который был объявлен как содержащий только D .
Причина, по которой это не очевидно из вашего исходного примера, заключается в потому что вы не создали отдельную переменную, которая ссылалась на объект, используя более строгую типизацию E
Дело в том, что List
в основном определяет новый класс, как и List
. Даже когда c расширяет Object, это не означает, что List
расширяет Список
, и это необходимо для работы задания.
Несмотря на то, что эта серия статей написана для платформы .NET, описание проблемы по-прежнему актуально для универсальных Java-шаблонов.
При присвоении переменной ( E
) с универсальным типом без подстановочных знаков T
, назначаемый объект должен иметь точно T
в качестве своего универсального типа (включая все параметры универсального типа T
, подстановочный знак и без подстановочного знака). В вашем случае T
- это D
, что отличается от типа D расширяет C>
.
Что вы можете сделать, потому что D
может быть назначен D расширяет C>
, используется тип подстановочного знака:
E<? extends D<? extends C>> a = new E<D<A>();