13

请解释这个通用代码通配符编译时错误:

//no compile time error.
List<? extends Number> x = new ArrayList<>(); 

//compile time error.  
List<? extends Number> x = new ArrayList<? extends Number>();
4

2 回答 2

27

使用通配符实例化泛型类型是无效的语法。类型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# 中,泛型类型信息在运行时保留 - 并且该语言中不存在泛型通配符。

于 2012-02-05T07:46:41.250 回答
1

利用

 List<? extends Number> x = new ArrayList<Number>();

反而。

于 2012-02-05T07:13:01.337 回答