簡體   English   中英

是列表 <Double> 列表的子類型 <? extends Number> 為什么?

[英]Is List<Double> a subtype of List<? extends Number> and why?

這就是我所知道的:

  1. DoubleNumber的子類型, List<Double>不是List<Number>的子類型。
  2. List<Dog>不是List<Animal>的子類型,因為您可以將Cat添加到List<Animal>但不能使用List<Dog>
  3. List<? extends Number> List<? extends Number>表示此列表可以存儲Number類型的變量和Number子類型的變量。 List<Double>表示此列表可以存儲Double類型的變量。

如果上面的任何錯誤,請糾正我,然后是List<Double> List<? extends Number>的子類型List<? extends Number> List<? extends Number>為什么?

你的所有物品都是正確的。

  1. DoubleNumber的子類型, List<Double>不是List<Number>的子類型。

  2. List<Dog>不是List<Animal>的子類型,因為您可以將Cat添加到List<Animal>但不能使用List<Dog>

那是對的。 泛型不是協變的(但數組是!)。 以下是一些后續閱讀: 為什么數組是協變的但是泛型是不變的?

  1. 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的列表混合DoubleInteger ,......

至於你的最后一個問題:

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) - TU類型在前面的陳述中是不同的

還應該添加幾點

  1. 在運行時List<Double>List<? extends Number> List<? extends Number>List<?>List<Object>都是相同的。 通用參數根本不編譯。 所有使用泛型的魔法都是編譯時的樂趣。 這也意味着如果你有一個空的List你不知道泛型參數是什么!

  2. 盡量不要將泛型參數視為“子類型”,泛型參數實際上意味着“ 使用泛型參數 ”,因此在這種情況下“ 列表使用數字 ”。 一個很好的例子就是HashMap源代碼 ,如果你看一下它的內部工作原理,它實際上是存儲一個Entry數組,並且所有條目都存儲了鍵和值。 當你看到泛型的更復雜的用途時,你偶爾會看到這種用法。

    List的情況下,泛型參數意味着列表存儲該類型的對象,可能是對象永遠不會存儲泛型參數類型的對象! 像這樣:

     public class DummyIterator<O> implements Iterator<O>{ public boolean hasNext() { return false; } public O next() { return null; } } 
  3. 列什么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類型

  4. 那么你應該在哪里使用? 你真的可以說“我不關心通用參數”,例如:

     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); } 
  5. 而不是使用? ? 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(); 
  6. 作為泛型的獎勵思想,您還可以參數化方法以返回特定類。 一個很好的例子是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.

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