以下是我所知道的:
Double
是Number
的子类型,List<Double>
不是List<Number>
的子类型。List<Dog>
不是List<Animal>
的子类型,因为您可以将Cat
添加到List<Animal>
,但不能使用List<Dog>
执行此操作。List<? extends Number>
表示此列表可以存储Number类型的变量和Number的子类型的变量。 List<Double>
表示此列表可以存储Double类型的变量。如果上述内容有误,请更正我,然后List<Double>
是[{1}}的子类型,为什么?
答案 0 :(得分:11)
您的所有商品都是正确的。
.col-half:nth-child(even) .thumb:after{ //some codes } .col-half:nth-child(odd) .thumb:after{ //some codes }
是Double
的子类型,Number
不是List<Double>
的子类型。- 醇>
List<Number>
不是List<Dog>
的子类型,因为您可以将List<Animal>
添加到Cat
,但您无法使用List<Animal>
执行此操作}。
那是对的。泛型不是协变的(但数组是!)。这里有一些跟进阅读:Why are arrays covariant but generics are invariant?
- 醇>
List<Dog>
表示此列表可以存储List<? extends Number>
类型的变量和Number
子类型的变量。Number
表示此列表可以存储List<Double>
类型的变量。
这是事实,但Double
和List<Number>
之间存在重要差异。您可以将List<? extends Number>
视为特定List<? extends Number>
- 子类型的列表(Number
,List<Double>
,List<Integer>
,...之一,并且List<Long>
作为列表,可能包含List<Number>
,Double
,...
至于你的最后一个问题:
Integer
的子类型是List<Double>
...
是的,你可以拥有
List<? extends Number>
......为什么?
这就是定义子类型的方式。
至于动机,假设你有一个接受数字列表的方法。如果您让参数的类型为List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
,则您无法将List<Number>
传递给它。 (您的问题中的第二项解释了原因!)相反,您可以让参数具有类型List<Double>
。由于List<? extends Number>
是List<Double>
的子类型,因此可以解决问题。
答案 1 :(得分:6)
在运行时,List<T>
和List<U>
与List
(1)相同。
然而,这将随着值类型的引入而改变(预计将在JDK 9或JDK 10版本中发布,而不是在2016年中期之前)。由于Brian Goetz在此解释了许多限制,List<T>
将不再与List<U>
相同:http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html
(1) - T
和U
类型在之前的陈述中有所不同
答案 2 :(得分:6)
还应该添加几点
在运行时List<Double>
,List<? extends Number>
,List<?>
和List<Object>
都是相同的。通用参数根本不编译。所有使用泛型的魔法都是编译时的乐趣。这也意味着,如果您的List
为空,则您不知道通用参数是什么!
尽量不要将泛型参数视为“子类型”,泛型参数实际上意味着“类使用泛型参数”所以在此case“ list 使用 Number ”。一个很好的例子就是HashMap source,如果你仔细研究它的内部工作原理,它实际上存储了一个Entry
数组,并且所有条目都存储了密钥和值。当你看到泛型的更复杂的用法时,你偶尔会看到这种用法。
在List
的情况下,泛型参数意味着列表存储了该类型的对象,可能是对象永远不会存储泛型参数类型的对象!像这样:
public class DummyIterator<O> implements Iterator<O>{
public boolean hasNext() {
return false;
}
public O next() {
return null;
}
}
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>
。但是对于这个例子:
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>
,因为您明确告诉它,该列表仅使用数字类型
那么你应该在哪里使用?
?你真的可以说“我不关心通用参数”,例如:
boolean equalListSizes(List<?> list1, List<?> list2) {
return list1.size() == list2.size();
}
您只能在使用generic参数不修改对象的情况下使用? extends Number
类型的格式。例如:
Number firstItem(List<? extends Number> list1) {
return list1.get(0);
}
不要使用?
和? 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();
此语法允许您指定对象的返回类型。有时知道非常有用(使一些铸造多余)!
答案 3 :(得分:3)
它帮助我将泛型视为约束或契约,而不是具有子类型的类型。
因此变量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>
时,这个列表的目的是什么?它允许您使用精确类型与手头任务无关的列表:
// 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
可以放松刚性合约到较弱的条件。
使用命名类型参数,您可以在多个变量之间建立对允许类型的约束:
// 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);