请解释这个通用代码通配符编译时错误:
//no compile time error.
List<? extends Number> x = new ArrayList<>();
//compile time error.
List<? extends Number> x = new ArrayList<? extends Number>();
请解释这个通用代码通配符编译时错误:
//no compile time error.
List<? extends Number> x = new ArrayList<>();
//compile time error.
List<? extends Number> x = new ArrayList<? extends Number>();
使用通配符实例化泛型类型是无效的语法。类型List<? extends Number>
表示某种类型List
的a或 extends 。创建这种类型的实例没有意义,因为通过实例化您正在创建特定的东西:Number
new ArrayList<? extends Number>();//compiler:"Wait, what am I creating exactly?"
带有通配符的泛型类型只对变量和方法参数有意义,因为这允许更大的自由度来分配/传递给它们。
//compiler:"Okay, so passing in a List<Integer> or a List<Double> are both fine"
public void eatSomeNumbers(List<? extends Number> numbers) {
for (Number number : numbers) {
System.out.println("om nom " + number + " nom");
}
}
请务必牢记使用通配符带来的限制。
List<? extends Number> numList = ...
numList.add(new Integer(3));//compiler:"Nope, cause that might be a List<Double>"
至于您的第一个示例,菱形是 Java 7 中的一项新功能,它允许编译器根据分配给它的变量的类型来推断新泛型实例的类型。在这种情况下:
List<? extends Number> x = new ArrayList<>();
编译器很可能new ArrayList<Number>()
在这里进行推断,但推断的内容并不重要,只要它是对给定变量的有效赋值即可。这就是引入菱形运算符的原因 - 指定新对象的泛型类型是多余的,只要某些泛型类型使其成为有效的赋值/参数。
这个推理只有在你记得 Java 中的泛型是纯粹的编译时语言特性时才有意义,因为类型擦除,并且在运行时没有意义。通配符的存在只是因为这个限制。相比之下,在 C# 中,泛型类型信息在运行时保留 - 并且该语言中不存在泛型通配符。
利用
List<? extends Number> x = new ArrayList<Number>();
反而。