Есть ли разница между
List<Map<String, String>>
и
List<? extends Map<String, String>>
?
Если нет разницы, в чем польза от использования ? extends
?
java
generics
inheritance
polymorphism
Eng.Fouad
источник
источник
Ответы:
Разница в том, что, например,
это
но не
Так:
Вы могли бы подумать, что a
List
ofHashMap
s должно быть aList
ofMap
s, но есть веская причина, почему это не так:Предположим, вы могли бы сделать:
Вот почему a
List
ofHashMap
s не должно быть aList
ofMap
s.источник
HashMap
этоMap
связано с полиморфизмом.List
ofHashMap
s не aList
ofMap
s.List<Map<String,String>> maps = hashMaps;
иHashMap<String,String> aMap = new HashMap<String, String>();
, вы все равно обнаружите, чтоmaps.add(aMap);
это незаконно, хотяhashMaps.add(aMap);
это законно. Цель состоит в том, чтобы предотвратить добавление неправильных типов, но это не позволит добавить правильные типы (компилятор не может определить «правильный» тип во время компиляции)HashMap
в списокMap
s, оба ваших примера являются законными, если я их правильно читаю.Вы не можете назначать выражения с такими типами, как
List<NavigableMap<String,String>>
первый.(Если вы хотите знать , почему вы не можете назначить
List<String>
дляList<Object>
увидим мильона других вопросов на SO.)источник
List<String>
это не подтипList<Object>
? - см., например, stackoverflow.com/questions/3246137/…? extends
. Он также не объясняет корреляцию с супер / подтипами или ко / контравариантностью (если таковая имеется).В других ответах мне не хватает ссылки на то, как это относится к ко- и контравариантности, а также к под- и супертипам (то есть полиморфизму) в целом и к Java в частности. ОП может это хорошо понять, но на всякий случай вот оно:
ковариации
Если у вас есть класс
Automobile
, тоCar
иTruck
есть их подтипы. Любой автомобиль можно присвоить переменной типа «Автомобиль», это хорошо известно в объектно ориентированном стиле и называется полиморфизмом. Ковариация относится к использованию того же принципа в сценариях с обобщенными элементами или делегатами. У Java нет делегатов (пока), поэтому этот термин применяется только к дженерикам.Я склонен думать о ковариации как о стандартном полиморфизме, который, как вы ожидаете, будет работать, не задумываясь, потому что:
Однако причина ошибки верна:
List<Car>
не наследуютсяList<Automobile>
и, следовательно, не могут быть присвоены друг другу. Только параметры универсального типа имеют отношение наследования. Можно подумать, что компилятор Java просто недостаточно умен, чтобы правильно понять ваш сценарий. Однако вы можете помочь компилятору, дав ему подсказку:контрвариация
Обратной стороной ковариации является контравариантность. Если в ковариации типы параметров должны иметь отношение подтипов, в контравариантности они должны иметь отношение супертипов. Это можно рассматривать как верхнюю границу наследования: разрешен любой супертип, включая указанный тип:
Это можно использовать с Collections.sort :
Вы даже можете вызвать его с помощью компаратора, который сравнивает объекты и использует его с любым типом.
Когда использовать противодействие или ковариацию?
Немного ОТ, возможно, вы не спрашивали, но это помогает понять ответ на ваш вопрос. В общем, когда вы что-то получаете , используйте ковариацию, а когда что- то помещаете , используйте контравариантность. Лучше всего это объяснить в ответе на вопрос о переполнении стека. Как контравариантность использоваться в дженериках Java?,
Так что же тогда с
List<? extends Map<String, String>>
Вы используете
extends
, поэтому применяются правила ковариации . Здесь у вас есть список карт, и каждый элемент, который вы храните в списке, должен бытьMap<string, string>
или производным от него. ОператорList<Map<String, String>>
не может извлечь изMap
, но должен бытьMap
.Следовательно, будет работать следующее, потому что
TreeMap
наследуется отMap
:но этого не будет:
и это тоже не сработает, потому что не удовлетворяет ковариационному ограничению:
Что еще?
Это, вероятно, очевидно, но вы, возможно, уже заметили, что использование
extends
ключевого слова применяется только к этому параметру, а не ко всем остальным. Т.е. не будут компилироваться:Предположим, вы хотите разрешить любой тип на карте с ключом в виде строки, который вы можете использовать
extend
для каждого параметра типа. То есть, предположим, что вы обрабатываете XML и хотите сохранить AttrNode, Element и т. Д. На карте, вы можете сделать что-то вроде:источник
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
приводит кfound: ? extends java.util.Map<java.lang.String,java.lang.String> required: class or interface without bounds
.List<Map<String, String>> mapList = new ArrayList<Map<String, String>>(); mapList.add(new TreeMap<String, String>());
работает отлично. Последний пример, очевидно, верен.Сегодня я использовал эту функцию, поэтому вот мой очень свежий пример из реальной жизни. (Я изменил имена классов и методов на общие, чтобы они не отвлекали от сути дела.)
У меня есть метод , который означал , чтобы принять
Set
изA
объектов , которые я изначально писал с этой подписью:Но на самом деле он хочет назвать это
Set
s подклассовA
. Но это недопустимо! (Причина в том, что к нимmyMethod
можно добавлять объектыset
того типаA
, но не подтипа,set
объекты которого объявлены как находящиеся на сайте вызывающего абонента. Так что это могло бы нарушить систему типов, если бы это было возможно.)Теперь на помощь приходят дженерики, потому что они работают, как и предполагалось, если я вместо этого использую эту сигнатуру метода:
или короче, если вам не нужно использовать фактический тип в теле метода:
Таким образом,
set
тип становится набором объектов фактического подтипаA
, поэтому становится возможным использовать его с подклассами, не подвергая опасности систему типов.источник
Как вы упомянули, могут быть две версии определения списка ниже:
List<? extends Map<String, String>>
List<?>
2 очень открыта. Он может содержать любой тип объекта. Это может быть бесполезно, если вы хотите иметь карту определенного типа. На случай, если кто-то случайно поставит другой тип карты, например
Map<String, int>
,. Ваш потребительский метод может сломаться.Чтобы гарантировать, что он
List
может содержать объекты заданного типа, были введены универсальные шаблоны Java? extends
. Итак, в №1List
может содержаться любой объект, производный отMap<String, String>
типа. Добавление любого другого типа данных вызовет исключение.источник