Еще один общий вопрос Java
У меня есть следующий класс:
interface Able{/* ... */}
class A implements Able{/* ... */}
и я имею
Map<String,? extends Able> as;
as = new HashMap<String, A>();
почему следующее вызывает ошибку:
as.put("a", new A());
Есть идеи?
6 ответов
Ссылка на дженерики java хорошая ( сайт jdk).
Действительно @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
Обратите внимание, что наследование является гиперонимическим отношением (обобщение / специализация), а коллекции определяют меронимическое отношение (контейнер / контейнер). И объединить их формально - головная боль, хотя это довольно легко используют нечеткие существа, которых люди представляют, например, на французском языке речи: синекдок.:)
С 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
там.
Хороший способ понять проблему - прочитать, что означает подстановочный знак:
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
Декларация о
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 с содержимым {"key", новым A()} - это ошибка типа!
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>
), тип пока неизвестен.
Вы ищете объявление карты, все значения которой реализуют 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;
}
(опять же с ограничением, что мы не можем добавить новые пары ключ / значение на карту, которая возвращается этим методом).
Вы не можете вставить какой-либо объект любого типа в коллекцию, объявленную с использованием подстановочного знака '?'
Вы можете вставить только "ноль"
После того, как вы объявите коллекцию как List, компилятор не может знать, что безопасно добавлять SubAble.
Что делать, если Collection<SubSubAble>
был назначен на Collection<Able>
? Это было бы допустимым назначением, но добавление SubAble могло бы загрязнить коллекцию.
Как можно добавить элементы в универсальную коллекцию подстановочных знаков?