еще один общий вопрос Java - PullRequest
6 голосов
/ 28 июня 2011

У меня есть следующий класс:

interface Able{/* ... */}
class A implements Able{/* ... */}

, и у меня

Map<String,? extends Able> as;
as = new HashMap<String, A>();

, почему следующее вызывает ошибку:

as.put("a", new A());

Есть идеи?

Ответы [ 6 ]

17 голосов
/ 28 июня 2011

Ссылка на дженерики java хорошая ( jdk site ).

Действительно, @Oli_Charlesworth дал хороший ответ, но, возможно, этот будет более полным.

В Collection<? extends Able> вы не можете вставить ничего правильного.

Если у вас есть

class A implements Able {...}

и

class B implement Able {...}

Тогда Collection<? extends Able> - это супер тип обоих:

Collection<A>
Collection<B>

Таким образом, законно написать какое-то утверждение типа

//Code snippet 01
Collection< ? extends Able > list;
Collection<A> listA;
Collection<B> listB;
list = listA;
list = listB;

Это действительно причина, по которой существует подстановочный знак Collection<? extends Able>.

Но здесь все становится интереснее:

В Collection<A> вы можете вставлять только объекты A (включая подклассы). То же самое для Collection<B>. В обоих случаях вы не можете добавить что-то просто Able. Например:

//Code snippet 02
listA.add( new A() );  //valid at compile-time
listA.add( new B() );  //not valid at compile-time
listB.add( new B() );  //valid at compile-time
listB.add( new A() );  //not valid at compile-time

Таким образом, если вы сгруппируете то, что мы видели в code snippets 01 & 02, вы поймете, что компилятору абсолютно невозможно принять такое утверждение, как:

Collection< ? extends Able > list;
list.add( new A() );         //not allowed, will work only if list is List<A>
list.add( new B() );         //not allowed, will work only if list is List<B>

Так что да, супер тип Collection< ? extends Able > не принимает ничего добавить. Более общие типы предлагают пересечение функциональных возможностей подтипов, и, как таковые, меньше функций этого подтипа. Здесь мы теряем возможность добавлять A объектов и B объектов. Эти функции появятся позже в иерархии ... и это даже означает, что мы не можем ничего добавить в суперкласс Collection< ? extends Able >

Дополнительное замечание:

Также обратите внимание, что в Collection<Able> вы можете добавить все, что вы хотите, как это:

Collection< Able > list;
list.add( new A() );         //valid
list.add( new B() );         //valid

Но Collection<Able> не является суперклассом Collection<A> и Collection<B>. Это будет означать, как и в случае любого отношения наследования, что подклассы могут делать все, что может делать их суперкласс, поскольку наследование является специализацией. Таким образом, это будет означать, что мы можем добавить объекты A и B в оба подкласса Collection<A> и Collection<B>, но это не так. Так как это не суперкласс, вы не можете иметь:

Collection<Able> list;
Collection<A> listA;
Collection<B> listB;
list = listA;  //not valid because there is no inheritance hierarchy
list = listB;  //not valid because there is no inheritance hierarchy

Обратите внимание, что наследование является гиперонимическим отношением (обобщение / специализация), а коллекции определяют меронимическое отношение (контейнер / контейнер). И объединить их формально - головная боль, хотя это довольно легко используется нечеткими существами, которых люди представляют, например, на французском языке: synecdocque . :)

8 голосов
/ 28 июня 2011

С http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html:

Как обычно, нужно заплатить цену за гибкость использования подстановочные знаки. Это цена, что это теперь запрещено писать в [контейнер с подстановочными знаками]. Например, это не разрешено:

public void addRectangle(List<? extends Shape> shapes) {
    shapes.add(0, new Rectangle()); // Compile-time error!
}

Вы должны быть в состоянии понять, почему код выше запрещен. Тип второго параметра в shapes.add() является ? extends Shape - неизвестный подтип Shape. С тех пор, как мы не знаю, какой это тип, мы не знать, если это супертип Rectangle; это может или не может быть такой супертип, так что это не безопасно пройти туда Rectangle.

3 голосов
/ 28 июня 2011

Хороший способ понять проблему - прочитать, что означает подстановочный знак:

Map<String,? extends Able> as;

"Карта с ключами типа String и значениями одного типа , расширяющими Able."

Причина, по которой операции добавления не разрешены, заключается в том, что они" открывают дверь "для введения в коллекцию различных типов, что может противоречить системе ввода.например,

class UnAble implements Able;
Map<String,UnAble> unableMap = new HashMap<String,UnAble>();
Map<String,? extends Able> ableMap = unableMap;
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 

Правильное использование шаблона подстановки будет:

Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 

Map<String,A> ableMap = new HashMap<String,A>;
ableMap.put("willwork",new A());
processAble(as);
processAble(unableMap); // from the definition above
1 голос
/ 28 июня 2011

объявление

Map<String,? extends Able> as; 

означает «любую карту со строковыми ключами и значениями, являющимися подтипом Able».Так, например, вы можете сделать следующее:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>();

А теперь давайте посмотрим на этот пример:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
as.put("key", new A() );

Если бы это было правильно, вы бы закончили иметь HashMap с содержимым {"ключ", новый A ()} - это ошибка типа!

0 голосов
/ 28 июня 2011

Collection<?> является супертипом для всех видов коллекций . Это не коллекция , которая может содержать любой тип . По крайней мере, это было мое неправильное понимание всей концепции.

Мы можем использовать его там, где нам нет дела до универсального типа, как в этом примере:

public static void print(Collection<?> aCollection) {
  for (Object o:aCollection) {
    System.out.println(o);
  }
}

Если мы выбрали вместо этого подпись:

public static void print(Collection<Object> aCollection)

мы бы ограничились коллекциями типа Collection<Object> - другими словами, такой метод не принял бы значение типа Collection<String>.

Таким образом, тип Collection<?> - это , а не коллекция, которая может принимать любой тип . Это займет всего неизвестного типа . И поскольку мы не знаем этот тип (он неизвестен;)), мы никогда не сможем добавить значение, потому что ни один тип в Java не является подклассом неизвестного типа.

Если мы добавим границы (например, <? extends Able>), тип будет все еще unknown.

Вы ищете объявление карты, все значения которой реализуют интерфейс Able. Правильное объявление просто:

Map<String, Able> map;

Предположим, у нас есть два типа A и B, которые подкласс Able и две дополнительные карты

Map<String, A> aMap;
Map<String, B> bMap;

и хотим, метод, который возвращает любую карту, значения которой реализуют интерфейс Able: затем мы используем подстановочный знак:

public Map<String, ? extends Able> createAorBMap(boolean flag) {
 return flag ? aMap: bMap;
}

(опять же с ограничением, что мы не можем добавить новые пары ключ / значение на карту, возвращаемую этим методом).

0 голосов
/ 28 июня 2011

Вы не можете вставить какой-либо объект любого типа в коллекцию, объявленную с использованием подстановочного знака '?'

вы можете вставить только «null»

После того, как вы объявите коллекцию как List, компилятор не может знать, что безопасно добавлять SubAble.

Что, если Collection<SubSubAble> был присвоен Collection<Able>?Это было бы допустимым назначением, но добавление SubAble могло бы загрязнить коллекцию.

Как можно добавить элементы в универсальную коллекцию с подстановочными знаками?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...