List <Map <String,String >> vs List <? 扩展Map <String,String >>
有什么区别吗?
List<Map<String, String>>
和
List<? extends Map<String, String>>
?
如果没有区别,使用有什么好处? extends
? extends
?
不同的是,例如,一个
List<HashMap<String,String>>
是一个
List<? extends Map<String,String>>
但不是
List<Map<String,String>>
所以:
void withWilds( List<? extends Map<String,String>> foo ){} void noWilds( List<Map<String,String>> foo ){} void main( String[] args ){ List<HashMap<String,String>> myMap; withWilds( myMap ); // Works noWilds( myMap ); // Compiler error }
你会认为一个HashMap
List
应该是一个Map
List
,但是为什么它不是:
假设你可以这样做:
List<HashMap<String,String>> hashMaps = new ArrayList<HashMap<String,String>>(); List<Map<String,String>> maps = hashMaps; // Won't compile, // but imagine that it could Map<String,String> aMap = Collections.singletonMap("foo","bar"); // Not a HashMap maps.add( aMap ); // Perfectly legal (adding a Map to a List of Maps) // But maps and hashMaps are the same object, so this should be the same as hashMaps.add( aMap ); // Should be illegal (aMap is not a HashMap)
所以这就是为什么HashMap
List
不应该是Map
List
。
不能将types如List<NavigableMap<String,String>>
expression式分配给第一个。
(如果你想知道为什么你不能把List<String>
分配给List<Object>
那么在SO上看到其他几十个问题。)
我在其他答案中缺less的是一个参考,这是如何涉及到共同和反变换,以及一般的和超types(即多态),尤其是Java。 OP可以很好地理解这一点,但为了以防万一,
协方差
如果你有一个类的Automobile
,那么Car
和Truck
是他们的亚型。 任何汽车都可以被分配到一个汽车types的variables,这在OO中是众所周知的,被称为多态。 协变指在与generics或委托的场景中使用相同的原则。 Java没有委托(还),所以这个术语只适用于generics。
我倾向于把协变作为标准的多态来考虑,你会不经过思考而工作,因为:
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>
inheritance,因此不能相互分配。 只有genericstypes参数具有inheritance关系。 有人可能会认为,Java编译器根本不够聪明,无法正确理解你的场景。 不过,你可以通过给他一个提示来帮助编译器:
List<Car> cars; List<? extends Automobile> automobiles = cars; // no error
逆变
协方差的相反是逆变。 在协方差的地方,参数types必须有一个子types关系,相反,它们必须有一个超types关系。 这可以被看作是一个inheritance的上限:任何超types都被允许使用,包括指定的types:
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());
你甚至可以用比较器来调用它,比较对象并将其用于任何types。
何时使用矛盾或协变?
也许有点OT,你没有问,但它有助于理解回答你的问题。 一般来说,当你得到某些东西的时候,使用协variables,当你放了东西时,使用反变换。 这是在堆栈溢出问题的答案中最好的解释如何在Javagenerics中使用逆变? 。
那么与List<? extends Map<String, String>>
是什么List<? extends Map<String, String>>
List<? extends Map<String, String>>
您使用extends
,所以协方差的规则适用。 在这里你有一个地图列表,你存储在列表中的每个项目必须是一个Map<string, string>
或从它派生。 语句List<Map<String, String>>
不能从Map
派生,但必须是Map
。
因此,以下将起作用,因为TreeMap
从Map
inheritance:
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
假设你想在map中允许任何types,用一个键作为string,你可以在每个types参数上使用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>());
今天,我已经使用了这个function,所以这里是我非常新鲜的现实生活中的例子。 (我已经将类和方法名称更改为通用名称,所以他们不会从实际的angular度分散注意力。)
我有一个方法是为了接受我最初用这个签名写的一Set
对象:
void myMethod(Set<A> set)
但它实际上要用A
的子类Set
s来调用它。 但这是不允许的! (原因是, myMethod
可以添加对象来set
typesA
,但不是set
对象被声明在调用者的站点的子types,所以这可能会打破types系统,如果可能的话。 )
现在这里来generics的救援,因为它的工作原理,如果我使用这种方法签名,而不是:
<T extends A> void myMethod(Set<T> set)
或更短,如果您不需要使用方法体中的实际types:
void myMethod(Set<? extends A> set)
这样, set
的types就成为了A
的实际子types的A
对象的集合,所以可以在子类中使用它而不危及types系统。
如你所说,定义一个List可能有两个以下的版本:
-
List<? extends Map<String, String>>
-
List<?>
2非常开放。 它可以容纳任何对象types。 如果你想有一个给定types的地图,这可能没有用处。 如果有人不小心放置了不同types的地图,例如Map<String, int>
。 您的消费方法可能会中断。
为了确保List
能够容纳给定types的对象,引入了Javagenerics? extends
? extends
。 所以在#1中, List
可以包含从Map<String, String>
types派生的任何对象。 添加任何其他types的数据会引发exception。