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 ,那么CarTruck是他们的亚型。 任何汽车都可以被分配到一个汽车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

因此,以下将起作用,因为TreeMapMapinheritance:

 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可以添加对象来settypesA ,但不是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可能有两个以下的版本:

  1. List<? extends Map<String, String>>
  2. List<?>

2非常开放。 它可以容纳任何对象types。 如果你想有一个给定types的地图,这可能没有用处。 如果有人不小心放置了不同types的地图,例如Map<String, int> 。 您的消费方法可能会中断。

为了确保List能够容纳给定types的对象,引入了Javagenerics? extends ? extends 。 所以在#1中, List可以包含从Map<String, String>types派生的任何对象。 添加任何其他types的数据会引发exception。