В других ответах я упускаю ссылку на то, как это относится к ко- и контравариантности, а также к под- и супертипам (то есть к полиморфизму) в целом и к Java в частности.Это может быть хорошо понято ОП, но на всякий случай вот так:
Ковариация
Если у вас есть класс Automobile
, тогда Car
и Truck
являются ихподтипы.Любой Автомобиль может быть назначен переменной типа Автомобиль, это хорошо известно в ОО и называется полиморфизмом.Ковариантность относится к использованию этого же принципа в сценариях с генериками или делегатами.У Java нет делегатов (пока), поэтому этот термин применяется только к дженерикам.
Я склонен думать о ковариации как о стандартном полиморфизме, который вы ожидаете работать без размышлений, потому что:
List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.
Причина ошибки, однако, является правильной: List<Car>
не не наследуется от List<Automobile>
и поэтому не может быть назначен друг другу.Только параметры универсального типа имеют наследуемое отношение.Можно подумать, что компилятор Java просто недостаточно умен, чтобы правильно понять ваш сценарий.Однако вы можете помочь компилятору, дав ему подсказку:
List<Car> cars;
List<? extends Automobile> automobiles = cars; // no error
Контравариантность
Обратной стороной ко-дисперсии является контрвариантность.Если в ковариации типы параметров должны иметь отношение подтипа, то в противоположность они должны иметь отношение супертипа.Это можно рассматривать как верхнюю границу наследования: разрешен любой супертип, включая указанный тип:
class AutoColorComparer implements Comparator<Automobile>
public int compare(Automobile a, Automobile b) {
// Return comparison of colors
}
Это можно использовать с Collections.sort :
public static <T> void sort(List<T> list, Comparator<? super T> c)
// Which you can call like this, without errors:
List<Car> cars = getListFromSomewhere();
Collections.sort(cars, new AutoColorComparer());
Вы даже можете вызвать его с помощью компаратора, который сравнивает объекты и использует его с любым типом.
Когда использовать контраст или ко-дисперсию?
Возможно, немного OT, выне спрашивал, но помогает понять, отвечая на ваш вопрос.В общем, когда вы получаете что-то, используйте ковариацию, а когда вы кладете что-то, используйте контравариантность.Это лучше всего объяснить в ответе на вопрос переполнения стека Как будет использоваться контравариантность в обобщениях Java? .
Так что же тогда с List<? extends Map<String, String>>
Вы используете extends
, поэтому применяются правила для ковариации .Здесь у вас есть список карт, и каждый элемент, который вы сохраняете в списке, должен быть Map<string, string>
или производным от него.Оператор List<Map<String, String>>
не может быть производным от Map
, но должен быть a Map
.
Следовательно, будет работать следующее, потому что TreeMap
наследуется от Map
:
List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());
но это не будет:
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());
, и это также не будет работать, потому что оно не удовлетворяет ковариационному ограничению:
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new ArrayList<String>()); // This is NOT allowed, List does not implement Map
Что еще?
Это, вероятно, очевидно, но вы, возможно, уже заметили, что использование ключевого слова extends
относится только к этому параметру, а не ко всем остальным.То есть следующее не скомпилируется:
List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>()) // This is NOT allowed
Предположим, вы хотите разрешить любой тип на карте, с ключом в качестве строки, вы можете использовать extend
для каждого параметра типа.То есть, если вы обрабатываете XML и хотите сохранить AttrNode, Element и т. Д. На карте, вы можете сделать что-то вроде:
List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;
// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());