![](/img/trans.png)
[英]Why List<Map<? extends Number,? extends Number>> is not working for List<Map<Integer,Double>>
[英]Is List<Double> a subtype of List<? extends Number> and why?
這就是我所知道的:
Double
是Number
的子類型, List<Double>
不是List<Number>
的子類型。 List<Dog>
不是List<Animal>
的子類型,因為您可以將Cat
添加到List<Animal>
但不能使用List<Dog>
。 List<? extends Number>
List<? extends Number>
表示此列表可以存儲Number類型的變量和Number子類型的變量。 List<Double>
表示此列表可以存儲Double類型的變量。 如果上面的任何錯誤,請糾正我,然后是List<Double>
List<? extends Number>
的子類型List<? extends Number>
List<? extends Number>
為什么?
你的所有物品都是正確的。
Double
是Number
的子類型,List<Double>
不是List<Number>
的子類型。
List<Dog>
不是List<Animal>
的子類型,因為您可以將Cat
添加到List<Animal>
但不能使用List<Dog>
。
那是對的。 泛型不是協變的(但數組是!)。 以下是一些后續閱讀: 為什么數組是協變的但是泛型是不變的?
List<? extends Number>
List<? extends Number>
表示此列表可以存儲Number
類型的變量和Number
子類型的變量。List<Double>
表示此列表可以存儲Double
類型的變量。
這是事實,但List<Number>
和List<? extends Number>
之間存在重要區別List<? extends Number>
List<? extends Number>
。 你能想到List<? extends Number>
List<? extends Number>
為特定Number
-subtype( List<Double>
, List<Integer>
, List<Long>
,...之一)的List<Number>
,並將List<Number>
List<? extends Number>
為可能包含a的列表混合Double
, Integer
,......
至於你的最后一個問題:
List<Double>
是List<? extends Number>
的子類型List<? extends Number>
List<? extends Number>
...
是的,你可以擁有
List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
......為什么?
這就是定義子類型的方式。
至於動機,假設你有一個接受數字列表的方法。 如果讓參數的類型為List<Number>
,則無法將List<Double>
傳遞給它。 (您的問題中的第二項解釋了原因!)相反,您可以讓參數具有類型List<? extends Number>
List<? extends Number>
。 由於List<Double>
是List<? extends Number>
的子類型List<? extends Number>
List<? extends Number>
它將工作。
在運行時, List<T>
和List<U>
與List
(1)相同。
但是,這將隨着值類型的引入而改變(預計將在JDK 9或JDK 10版本中發布,而不是在2016年中期之前)。 List<T>
將不再與List<U>
相同,因為Brian Goetz在此解釋了許多約束: http : //cr.openjdk.java.net/~briangoetz/valhalla/specialization.html
(1) - T
和U
類型在前面的陳述中是不同的
還應該添加幾點
在運行時List<Double>
, List<? extends Number>
List<? extends Number>
, List<?>
和List<Object>
都是相同的。 通用參數根本不編譯。 所有使用泛型的魔法都是編譯時的樂趣。 這也意味着如果你有一個空的List
你不知道泛型參數是什么!
盡量不要將泛型參數視為“子類型”,泛型參數實際上意味着“ 類使用泛型參數 ”,因此在這種情況下“ 列表使用數字 ”。 一個很好的例子就是HashMap源代碼 ,如果你看一下它的內部工作原理,它實際上是存儲一個Entry
數組,並且所有條目都存儲了鍵和值。 當你看到泛型的更復雜的用途時,你偶爾會看到這種用法。
在List
的情況下,泛型參數意味着列表存儲該類型的對象,可能是對象永遠不會存儲泛型參數類型的對象! 像這樣:
public class DummyIterator<O> implements Iterator<O>{ public boolean hasNext() { return false; } public O next() { return null; } }
列什么List<? extends Number>
List<? extends Number>
實際意味着什么? 對於大多數用途,它與List<Number>
幾乎相同。 請記住,通過說?
你幾乎都說'我不關心這種類型'在這種情況下表現出來:
List<Double> doubles = new ArrayList<Double>(); List<? extends Number> numbers = doubles; numbers.add(new Double(1)); //COMPILE ERROR Number num = numbers.get(0);
所以我們不能添加一個Double
到一個<? extends Number>
<? extends Number>
。 但是對於這個例子:
List<Double> doubles = new ArrayList<Double>(); List<Number> numbers = doubles; //COMPILE ERROR numbers.add(new Integer(1)); Number num = numbers.get(0);
您無法將List<Double>
分配給List<Number>
,因為您明確告訴它,該列表僅使用Number類型
那么你應該在哪里使用?
? 你真的可以說“我不關心通用參數”,例如:
boolean equalListSizes(List<?> list1, List<?> list2) { return list1.size() == list2.size(); }
你會用的? extends Number
僅在您不使用generic參數修改對象的情況下, ? extends Number
格式類型。 例如:
Number firstItem(List<? extends Number> list1) { return list1.get(0); }
而不是使用?
和? extends Number
? extends Number
格式嘗試在類/方法上使用泛型,在大多數情況下,它使您的代碼更具可讀性!:
<T extends Number> T firstItem(List<T> list1) { return list1.get(0); }
類:
class Animal{} class Dog extends Animal{} class AnimalHouse<A extends Animal> { List<A> animalsInside = new ArrayList<A>(); void enterHouse(A animal){ animalsInside.add(A); } A leaveHouse() { return animalsInside.remove(0); } } AnimalHouse<Dog> ah = new AnimalHouse<Dog>(); ah.enterHouse(new Dog()); Dog rufus = ah.leaveHouse();
作為泛型的獎勵思想,您還可以參數化方法以返回特定類。 一個很好的例子是junit中的any()方法和空列表集合:
Dog rufus = Matchers.<Dog>any(); List<Dog> dogs = Collections.<Dog>emptyList();
此語法允許您指定對象的返回類型。 有時知道非常有用(使一些鑄造多余)!
它幫助我將泛型視為約束或契約,而不是具有子類型的類型。 那么變量List<? extends Number> var
List<? extends Number> var
說: var
是一些未知類型的列表?
,它被約束為Number的子類型。
List<Number> listN;
List<Double> listD;
List<? extends Number> listX;
...
Number n1 = ...;
Double d1 = ...;
...
listN.add(n1); // OK n1 is a Number
listN.add(d1); // OK d1 is a Double, which is a Number
listD.add(n1); // compile error, n1 is not a Double
listD.add(d1); // OK
listX.add(n1); // compile error, because the exact type of list is not known! (prevents putting a Dog in a Cat list)
listX.add(d1); // compile error, same cause
那么當你甚至無法將一個數字放入一個List<? extends Number>
List<? extends Number>
,這個列表的目的是什么? 它允許您使用精確類型與手頭任務無關的列表:
// instead of several exactly typed methods...
int count(List<Number> numberList) {...}
int count(List<Object> objectList) {...}
// ...etc. you can have one with a degree of freedom:
int count(List<?> anyList) {...} // don't need to know the exact type of list
// instead of this...
Number sum(List<Number> numberList) {...}
Number sum(List<Double> doubleList) {...}
Number sum(List<Integer> integerList){...}
// you can do this, with a little less freedom in the ?
Number sum(List<? extends Number> list) {
// the only thing we need to know about the list's type is that it is some number type
...
Number ni = list.get(i);
...
}
使用通配符? extends X
? extends X
允許將剛性合約放寬到較弱的條件。
使用命名類型參數,您可以在多個變量之間建立對允許類型的約束:
// works for any type T of list, whatever T is
// T is the same in the argument and in the return
<T> T pickObject(List<T> list, int index) {
return list.get(index);
}
// works for any type T of list, if T is a Number type
// T is the same in the argument and in the return
<T extends Number> T pickNumber(List<T> list, int index) {
return list.get(index);
}
...
List<Number> list;
Number n = pickNumber(list);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.