簡體   English   中英

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

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

兩者之間有什么區別嗎?

List<Map<String, String>>

List<? extends Map<String, String>>

如果沒有差異,使用的好處是? extends ? extends

不同之處在於,例如,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
}

你會認為HashMapList應該是MapList ,但是有一個很好的理由不是:

假設你可以這樣做:

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)

所以這就是為什么一個ListHashMap不是應該是一個ListMap秒。

您不能將包含List<NavigableMap<String,String>>等類型的表達式分配給第一個。

(如果你想知道為什么你不能指定List<String>List<Object>看到SO是數不勝數的其他問題。)

我在其他答案中缺少的是對一般情況和特別是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.

但是,錯誤的原因是正確的: 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<? extends Map<String, String>> 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>());

但這不會:

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

假設您希望允許地圖中的任何類型,使用鍵作為字符串,您可以對每個類型參數使用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>());

今天,我已經使用了這個功能,所以這是我非常新鮮的現實生活中的例子。 (我已將類和方法名稱更改為通用名稱,因此它們不會分散實際意義。)

我有一個方法,它意味着接受我最初使用此簽名編寫的一Set A對象:

void myMethod(Set<A> set)

但是它實際上想要用A的子類Set來調用它。 但這是不允許的! (原因是, myMethod可以添加set A類型的對象,但不能set的對象聲明在調用者站點的子類型。所以如果可能的話,這可能會破壞類型系統。 )

現在這里有一些泛型來拯救,因為如果我使用這種方法簽名,它會按預期工作:

<T extends A> void myMethod(Set<T> set)

或更短,如果您不需要使用方法體中的實際類型:

void myMethod(Set<? extends A> set)

這樣, set的類型成為A的實際子類型的對象的集合,因此可以將其與子類一起使用而不會危及類型系統。

如您所述,定義List可能有兩個以下版本:

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

2是非常開放的。 它可以容納任何對象類型。 如果您想要一個給定類型的地圖,這可能沒用。 如果有人意外地放置了不同類型的地圖,例如Map<String, int> 您的消費者方法可能會中斷

為了確保List可以保存給定類型的對象,引入了Java泛型? extends ? extends 所以在#1中, List可以包含從Map<String, String>類型派生的任何對象。 添加任何其他類型的數據都會引發異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM