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

问题

两者之间有什么区别吗?

List<Map<String, String>>

List<? extends Map<String, String>>

如果没有差异,使用? extends有什么好处?


#1 热门回答(173 赞)

不同之处在于,例如,a

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
}

你会认为aListofHashMaps应该是aListofMaps,但有一个很好的理由不是:

假设你可以这样做:

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)

所以这就是为什么aListofHashMaps不应该是aListofMaps。


#2 热门回答(25 赞)

你不能将类型为1List<NavigableMap<String,String>>的表达式分配给第一个。

(如果你想知道为什么你不能在SO上分配List<String>List<Object>和其他问题。)


#3 热门回答(16 赞)

我在其他答案中缺少的是对一般情况下与共同和逆变量以及子类型和超类型(即多态性)的关系以及特别是Java的参考。 OP可能会很好地理解这一点,但为了以防万一,这里有:

##协方差

如果你有classAutomobile,那么CarTruck是他们的子类型。可以将任何Car分配给Automobile类型的变量,这在OO中是众所周知的并且被称为多态。协方差指的是在具有泛型或代表的场景中使用相同的原理。 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,你没有问,但它有助于理解回答你的问题。一般来说,当你想要什么时,使用协方差,当你输出一些东西时,使用逆变。最好在Stack Overflow问题的答案中解释如何在Java泛型中使用逆变?

##那么List <是什么呢?扩展Map <String,String >>

你使用extends,因此适用于协方差的规则。这里有一个 Map 列表,你在列表中存储的每个项目必须是aMap<string, string>或从中派生出来的。声明List<Map<String, String>>无法从Map派生,但必须是beaMap

因此,以下将起作用,因为来自Map的2149668inherits:

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 中的任何类型,使用键作为字符串,你可以在每个类型参数中使用extend。即,假设你处理XML并且想要在 Map 中存储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>());