简体   繁体   English

List <Map <String,String >> vs List <?扩展Map <String,String >>

[英]List<Map<String, String>> vs List<? extends Map<String, String>>

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 ? extends ? ? extends

The difference is that, for example, a 不同之处在于,例如,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: 你会认为HashMapList应该是MapList ,但是有一个很好的理由不是:

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. 所以这就是为什么一个ListHashMap不是应该是一个ListMap秒。

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 zillion other questions on SO.) (如果你想知道为什么你不能指定List<String>List<Object>看到SO是数不胜数的其他问题。)

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. 我在其他答案中缺少的是对一般情况和特别是Java的共同和逆变以及子类型和超类型(即多态性)的关系的参考。 This may be well understood by the OP, but just in case, here it goes: OP可能会很好地理解这一点,但为了以防万一,这里有:

Covariance 协方差

If you have a class Automobile , then Car and Truck are their subtypes. 如果您有Automobile类,那么CarTruck是他们的子类型。 Any Car can be assigned to a variable of type Automobile, this is well-known in OO and is called polymorphism. 可以将任何Car分配给Automobile类型的变量,这在OO中是众所周知的并且被称为多态。 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. 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 not inherit from List<Automobile> and thus cannot be assigned to each other. 但是,错误的原因是正确的: List<Car> List<Automobile>继承,因此不能相互分配。 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. 有人可能会认为Java编译器不够智能,无法正确理解您的场景。 However, you can help the compiler by giving him a hint: 但是,您可以通过给他一个提示来帮助编译器:

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. 也许有点OT,你没有问,但它有助于理解回答你的问题。 In general, when you get something, use covariance and when you put something, 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>> List<? extends Map<String, String>>

You use extends , so the rules for covariance applies. 您使用extends ,因此适用协方差规则。 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. 这里有一个地图列表,您在列表中存储的每个项目必须是Map<string, string>或从中派生。 The statement List<Map<String, String>> cannot derive from Map , but must be a Map . 声明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. 这可能很明显,但您可能已经注意到使用extends关键字仅适用于该参数而不适用于其他参数。 Ie, the following will not compile: 即,以下将无法编译:

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. 假设您希望允许地图中的任何类型,使用键作为字符串,您可以对每个类型参数使用extend Ie, suppose you process XML and you want to store AttrNode, Element etc in a map, you can do something like: 即,假设您处理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>());

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 . 但是它实际上想要用A的子类Set来调用它。 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.) (原因是, 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的实际子类型的对象的集合,因此可以将其与子类一起使用而不会危及类型系统。

As you mentioned, there could be two below versions of defining a List: 如您所述,定义List可能有两个以下版本:

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

2 is very open. 2是非常开放的。 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> . 如果有人意外地放置了不同类型的地图,例如Map<String, int> Your consumer method might break. 您的消费者方法可能会中断

In order to ensure that List can hold objects of a given type, Java generics introduced ? extends 为了确保List可以保存给定类型的对象,引入了Java泛型? extends ? extends . ? extends So in #1, the List can hold any object which is derived from Map<String, String> type. 所以在#1中, List可以包含从Map<String, String>类型派生的任何对象。 Adding any other type of data would throw an exception. 添加任何其他类型的数据都会引发异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM