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

Eng*_*uad 126 java generics polymorphism inheritance

两者之间有什么区别吗?

List<Map<String, String>>
Run Code Online (Sandbox Code Playgroud)

List<? extends Map<String, String>>
Run Code Online (Sandbox Code Playgroud)

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

tru*_*ity 176

不同之处在于,例如,a

List<HashMap<String,String>>
Run Code Online (Sandbox Code Playgroud)

是一个

List<? extends Map<String,String>>
Run Code Online (Sandbox Code Playgroud)

但不是

List<Map<String,String>>
Run Code Online (Sandbox Code Playgroud)

所以:

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
}
Run Code Online (Sandbox Code Playgroud)

你可能会认为一个ListHashMapS的关系是一个ListMapS,但有一个很好的理由,为什么它不是:

假设你可以这样做:

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)
Run Code Online (Sandbox Code Playgroud)

所以这就是为什么ListHashMap不是应该是一个ListMap秒.

  • 是的,但``HashMap`的`List`不是`Map`s的`List`. (45认同)
  • 由于多态性,`HashMap`仍然是一个`Map`. (6认同)
  • 我知道了.现在,很明显:).非常感谢+1. (2认同)

Tom*_*ine 25

您不能将类型的表达式分配List<NavigableMap<String,String>>给第一个类型.

(如果你想知道为什么你不能分配List<String>List<Object>看到数不胜数上的其他问题.)

  • @Samir解释什么?`List <String>`不是`List <Object>`的子类型? - 例如,参见http://stackoverflow.com/questions/3246137/java-generics-cannot-cast-listsubclass-to-listsuperclass (3认同)
  • 这并不能解释有没有`之间的区别?extends`.它也没有解释与超/子类型或共同/逆变(如果有的话)的相关性. (2认同)

Abe*_*bel 16

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

协方差

如果你有一个类Automobile,然后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.
Run Code Online (Sandbox Code Playgroud)

然而,错误的原因是正确的:List<Car> 继承List<Automobile>,因此不能彼此分配.只有泛型类型参数具有继承关系.有人可能会认为Java编译器不够智能,无法正确理解您的场景.但是,您可以通过给他一个提示来帮助编译器:

List<Car> cars;
List<? extends Automobile> automobiles = cars;   // no error
Run Code Online (Sandbox Code Playgroud)

逆变

协方差的逆转是逆向的.在协方差中,参数类型必须具有子类型关系,相反,它们必须具有超类型关系.这可以被视为继承上限:允许任何超类型并包括指定的类型:

class AutoColorComparer implements Comparator<Automobile>
    public int compare(Automobile a, Automobile b) {
        // Return comparison of colors
    }
Run Code Online (Sandbox Code Playgroud)

这可以与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());
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用比较器调用它来比较对象并将其与任何类型一起使用.

何时使用对抗或共同变化?

也许有点OT,你没有问,但它有助于理解回答你的问题.一般来说,当你得到某些东西时,使用协方差,当你放置东西时,使用逆变.最好在Stack Overflow问题的答案中解释如何在Java泛型中使用逆变?.

那么它是什么呢 List<? extends Map<String, String>>

您使用extends,因此协方差规则适用.这里有一个地图列表,您在列表中存储的每个项目必须是Map<string, string>或来自它.声明List<Map<String, String>>不能获得Map,但必须是一个 Map.

因此,以下内容将起作用,因为TreeMap继承自Map:

List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());
Run Code Online (Sandbox Code Playgroud)

但这不会:

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());
Run Code Online (Sandbox Code Playgroud)

这也不会起作用,因为它不满足协方差约束:

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
Run Code Online (Sandbox Code Playgroud)

还有什么?

这可能是显而易见的,但您可能已经注意到使用extends关键字仅适用于该参数而不适用于其他参数.即,以下将无法编译:

List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>())  // This is NOT allowed
Run Code Online (Sandbox Code Playgroud)

假设您希望允许地图中的任何类型,使用键作为字符串,您可以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>());
Run Code Online (Sandbox Code Playgroud)