Java List<Map<String, String>> vs List<? 扩展地图<字符串,字符串>>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9810445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
List<Map<String, String>> vs List<? extends Map<String, String>>
提问by Eng.Fouad
Is there any difference between
有什么区别吗
List<Map<String, String>>
and
和
List<? extends Map<String, String>>
?
?
If there is no difference, what is the benefit of using ? extends
?
如果没有区别,使用 有什么好处? extends
?
采纳答案by trutheality
The difference is that, for example, a
不同之处在于,例如,一个
List<HashMap<String,String>>
is a
是一个
List<? extends Map<String,String>>
but not a
但不是
List<Map<String,String>>
So:
所以:
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
}
You would think a List
of HashMap
s should be a List
of Map
s, but there's a good reason why it isn't:
你会认为 a List
of HashMap
s 应该是 a List
of Map
s,但有一个很好的理由为什么它不是:
Suppose you could do:
假设你可以这样做:
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)
So this is why a List
of HashMap
s shouldn't be a List
of Map
s.
所以这就是为什么 a List
of HashMap
s 不应该是 a List
of Map
s。
回答by Tom Hawtin - tackline
You cannot assign expressions with types such as List<NavigableMap<String,String>>
to the first.
您不能将具有类型的表达式分配List<NavigableMap<String,String>>
给第一个。
(If you want to know why you can't assign List<String>
to List<Object>
see a zillionother questions on SO.)
(如果你想知道为什么你不能分配List<String>
到List<Object>
看到数不胜数上的其他问题。)
回答by Java Spring Coder
As you mentioned, there could be two below versions of defining a List:
正如您所提到的,定义列表可能有以下两个版本:
List<? extends Map<String, String>>
List<?>
List<? extends Map<String, String>>
List<?>
2 is very open. It can hold any object type. This may not be useful in case you want to have a map of a given type. In case someone accidentally puts a different type of map, for example, Map<String, int>
. Your consumer method might break.
2 非常开放。它可以容纳任何对象类型。如果您想拥有给定类型的地图,这可能没有用。万一有人不小心放置了不同类型的地图,例如,Map<String, int>
。您的消费者方法可能会中断。
In order to ensure that List
can hold objects of a given type, Java generics introduced ? extends
. So in #1, the List
can hold any object which is derived from Map<String, String>
type. Adding any other type of data would throw an exception.
为了确保List
可以容纳给定类型的对象,Java 泛型引入了? extends
. 因此,在#1 中,List
可以容纳从Map<String, String>
类型派生的任何对象。添加任何其他类型的数据都会引发异常。
回答by R?rd
Today, I have used this feature, so here's my very fresh real-life example. (I have changed class and method names to generic ones so they won't distract from the actual point.)
今天,我已经使用了这个功能,所以这是我非常新鲜的现实生活中的例子。(我已将类和方法名称更改为通用名称,这样它们就不会分散实际的注意力。)
I have a method that's meant to accept a Set
of A
objects that I originally wrote with this signature:
我有一个方法可以接受我最初用这个签名编写Set
的A
对象:
void myMethod(Set<A> set)
But it want to actually call it with Set
s of subclasses of A
. But this is not allowed! (The reason for that is, myMethod
could add objects to set
that are of type A
, but not of the subtype that set
's objects are declared to be at the caller's site. So this could break the type system if it were possible.)
但它实际上想用Set
的子类 s来调用它A
。但这是不允许的!(这样做的原因是,myMethod
可以添加set
类型为 的对象A
,但不能添加set
声明为调用方站点的对象的子类型。因此,如果可能的话,这可能会破坏类型系统。)
Now here come generics to the rescue, because it works as intended if I use this method signature instead:
现在泛型来救援,因为如果我使用这个方法签名,它会按预期工作:
<T extends A> void myMethod(Set<T> set)
or shorter, if you don't need to use the actual type in the method body:
或更短,如果您不需要在方法主体中使用实际类型:
void myMethod(Set<? extends A> set)
This way, set
's type becomes a collection of objects of the actual subtype of A
, so it becomes possible to use this with subclasses without endangering the type system.
这样,set
的类型成为 的实际子类型的对象的集合A
,因此可以在不危及类型系统的情况下将 this 与子类一起使用。
回答by Abel
What I'm missing in the other answers is a reference to how this relates to co- and contravariance and sub- and supertypes (that is, polymorphism) in general and to Java in particular. This may be well understood by the OP, but just in case, here it goes:
我在其他答案中缺少的是关于这通常与协变和逆变以及子类型和超类型(即多态性),特别是与 Java 的关系的参考。OP 可能会很好地理解这一点,但以防万一,这里是:
Covariance
协方差
If you have a class Automobile
, then Car
and Truck
are their subtypes. Any Car can be assigned to a variable of type Automobile, this is well-known in OO and is called polymorphism. Covariance refers to using this same principle in scenarios with generics or delegates. Java doesn't have delegates (yet), so the term applies only to generics.
如果你有一个 class Automobile
,那么Car
andTruck
是它们的子类型。任何 Car 都可以分配给一个类型为 Automobile 的变量,这在 OO 中是众所周知的,称为多态。协方差是指在具有泛型或委托的场景中使用相同的原则。Java 还没有委托(还没有),因此该术语仅适用于泛型。
I tend to think of covariance as standard polymorphism what you would expect to work without thinking, because:
我倾向于将协方差视为标准的多态性,您可以不假思索地工作,因为:
List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.
The reason of the error is, however, correct: List<Car>
does notinherit from List<Automobile>
and thus cannot be assigned to each other. Only the generic type parameters have an inherit relationship. One might think that the Java compiler simply isn't smart enough to properly understand your scenario there. However, you can help the compiler by giving him a hint:
然而,错误的原因是正确的:List<Car>
不继承自List<Automobile>
,因此不能相互分配。只有泛型类型参数具有继承关系。有人可能认为 Java 编译器不够聪明,无法正确理解您的场景。但是,您可以通过给编译器一个提示来帮助他:
List<Car> cars;
List<? extends Automobile> automobiles = cars; // no error
Contravariance
逆变
The reverse of co-variance is contravariance. Where in covariance the parameter types must have a subtype relationship, in contravariance they must have a supertype relationship. This can be considered as an inheritance upper-bound: any supertype is allowed up and including the specified type:
协方差的反面是逆变。在协变中,参数类型必须具有子类型关系,而在逆变中,它们必须具有超类型关系。这可以被视为继承上限:允许任何超类型并包括指定的类型:
class AutoColorComparer implements Comparator<Automobile>
public int compare(Automobile a, Automobile b) {
// Return comparison of colors
}
This can be used with Collections.sort:
这可以与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());
You could even call it with a comparer that compares objects and use it with any type.
您甚至可以使用比较器来调用它,该比较器比较对象并将其用于任何类型。
When to use contra or co-variance?
何时使用反差或协方差?
A bit OT perhaps, you didn't ask, but it helps understanding answering your question. In general, when you getsomething, use covariance and when you putsomething, use contravariance. This is best explained in an answer to Stack Overflow question How would contravariance be used in Java generics?.
也许有点过时,你没有问,但它有助于理解回答你的问题。一般来说,当你得到某物时,使用协方差,当你放入某物时,使用逆变。这在Stack Overflow 问题的答案中得到了最好的解释,Java 泛型中如何使用逆变?.
So what is it then with List<? extends Map<String, String>>
那又是什么呢? List<? extends Map<String, String>>
You use extends
, so the rules for covarianceapplies. Here you have a list of maps and each item you store in the list must be a Map<string, string>
or derive from it. The statement List<Map<String, String>>
cannot derive from Map
, but must be aMap
.
您使用extends
,因此适用协方差规则。这里有一个地图列表,您存储在列表中的每个项目都必须是Map<string, string>
它的一个或派生自它。声明List<Map<String, String>>
不能获得Map
,但必须是一个Map
。
Hence, the following will work, because TreeMap
inherits from Map
:
因此,以下将起作用,因为TreeMap
继承自Map
:
List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());
but this will not:
但这不会:
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());
and this will not work either, because it does not satisfy the covariance constraint:
这也不起作用,因为它不满足协方差约束:
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
What else?
还有什么?
This is probably obvious, but you may have already noted that using the extends
keyword only applies to that parameter and not to the rest. I.e., the following will not compile:
这可能是显而易见的,但您可能已经注意到,使用extends
关键字仅适用于该参数而不适用于其余参数。即,以下将无法编译:
List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>()) // This is NOT allowed
Suppose you want to allow any type in the map, with a key as string, you can use extend
on each type parameter. I.e., suppose you process XML and you want to store AttrNode, Element etc in a map, you can do something like:
假设您想允许映射中的任何类型,以一个键作为字符串,您可以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>());