繁体   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