List<Double> 是 List< 的子类型吗? extends Number> 为什么?
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>
表示这个列表可以存放Number类型的变量和Number子类型的变量。 List<Double>
表示这个列表可以存储Double类型的变量。
如果以上内容有误,请指正 List<Double>
是 List<? extends Number>
的子类型吗?
你所有的项目都是正确的。
Double
is a subtype of Number
and List<Double>
is not a subtype of List<Number>
.
List<Dog>
is not a subtype of List<Animal>
because you can add Cat
to List<Animal>
but you can't do that with List<Dog>
.
没错。泛型不是协变的(但数组是!)。这是一些后续阅读:Why are arrays covariant but generics are invariant?
List<? extends Number>
means this list can store variables of type Number
and variables of subtype of Number
. List<Double>
means this list can store variables of type Double
.
这是事实,但 List<Number>
和 List<? extends Number>
之间有一个重要的区别。您可以将 List<? extends Number>
视为特定 Number
-子类型(即 List<Double>
、List<Integer>
、List<Long>
、...之一)的列表,并且List<Number>
作为可能包含 Double
、Integer
、...
的列表
最后一个问题:
Is List<Double>
a subtype of List<? extends Number>
...
是的,你可以有例如
List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
... and why?
这就是定义子类型的方式。
至于动机,假设您有一个接受数字列表的方法。如果您让参数具有 List<Number>
类型,您将无法将 List<Double>
传递给它。 (问题中的第二项解释了原因!)相反,您可以让参数的类型为 List<? extends Number>
。因为 List<Double>
是 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<? 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);
还有几点需要补充
在运行时 List<Double>
、List<? extends Number>
、List<?>
和 List<Object>
都是相同的。根本不编译通用参数。泛型的所有魔力都是编译时的乐趣。这也意味着如果你有一个空的 List
你不知道通用参数是什么!
尽量不要将泛型参数想象成 "Subtype",泛型参数的真正含义是 "the class uses a generic parameter",因此在本例中为 "the list uses a 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>
,这在您明确说明时是有意义的,列表仅使用数字类型
那么你应该在哪里使用?
?其实在任何地方你都可以说 "I don't care about the generic parameter" 例如:
boolean equalListSizes(List<?> list1, List<?> list2) {
return list1.size() == list2.size();
}
您只能在 不使用通用参数修改对象 的情况下使用 ? extends Number
类型的格式。例如:
Number firstItem(List<? extends Number> list1) {
return list1.get(0);
}
不要使用 ?
和 ? extends Number
格式,而是尝试在 class / 方法上使用泛型,在大多数情况下,它会使您的代码更具可读性好吧!:
<T extends Number> T firstItem(List<T> list1) {
return list1.get(0);
}
Class:
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();
作为围绕泛型的额外思考,您还可以将方法参数化为 return 特定 class。一个很好的例子是 junit 中的 any() 方法和空列表集合:
Dog rufus = Matchers.<Dog>any();
List<Dog> dogs = Collections.<Dog>emptyList();
此语法允许您指定对象的 return 类型。有时了解非常有用(使一些铸造变得多余)!
这是我所知道的:
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>
是 List<? extends Number>
的子类型吗?
你所有的项目都是正确的。
Double
is a subtype ofNumber
andList<Double>
is not a subtype ofList<Number>
.
List<Dog>
is not a subtype ofList<Animal>
because you can addCat
toList<Animal>
but you can't do that withList<Dog>
.
没错。泛型不是协变的(但数组是!)。这是一些后续阅读:Why are arrays covariant but generics are invariant?
List<? extends Number>
means this list can store variables of typeNumber
and variables of subtype ofNumber
.List<Double>
means this list can store variables of typeDouble
.
这是事实,但 List<Number>
和 List<? extends Number>
之间有一个重要的区别。您可以将 List<? extends Number>
视为特定 Number
-子类型(即 List<Double>
、List<Integer>
、List<Long>
、...之一)的列表,并且List<Number>
作为可能包含 Double
、Integer
、...
最后一个问题:
Is
List<Double>
a subtype ofList<? extends Number>
...
是的,你可以有例如
List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles;
... and why?
这就是定义子类型的方式。
至于动机,假设您有一个接受数字列表的方法。如果您让参数具有 List<Number>
类型,您将无法将 List<Double>
传递给它。 (问题中的第二项解释了原因!)相反,您可以让参数的类型为 List<? extends Number>
。因为 List<Double>
是 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<? 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);
还有几点需要补充
在运行时
List<Double>
、List<? extends Number>
、List<?>
和List<Object>
都是相同的。根本不编译通用参数。泛型的所有魔力都是编译时的乐趣。这也意味着如果你有一个空的List
你不知道通用参数是什么!尽量不要将泛型参数想象成 "Subtype",泛型参数的真正含义是 "the class uses a generic parameter",因此在本例中为 "the list uses a 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>
,这在您明确说明时是有意义的,列表仅使用数字类型那么你应该在哪里使用
?
?其实在任何地方你都可以说 "I don't care about the generic parameter" 例如:boolean equalListSizes(List<?> list1, List<?> list2) { return list1.size() == list2.size(); }
您只能在 不使用通用参数修改对象 的情况下使用
? extends Number
类型的格式。例如:Number firstItem(List<? extends Number> list1) { return list1.get(0); }
不要使用
?
和? extends Number
格式,而是尝试在 class / 方法上使用泛型,在大多数情况下,它会使您的代码更具可读性好吧!:<T extends Number> T firstItem(List<T> list1) { return list1.get(0); }
Class:
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();
作为围绕泛型的额外思考,您还可以将方法参数化为 return 特定 class。一个很好的例子是 junit 中的 any() 方法和空列表集合:
Dog rufus = Matchers.<Dog>any(); List<Dog> dogs = Collections.<Dog>emptyList();
此语法允许您指定对象的 return 类型。有时了解非常有用(使一些铸造变得多余)!