使用通配符创建新的通用对象

Sam*_*msh 11 java generics bounded-wildcard

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

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

//compile time error.  
List<? extends Number> x = new ArrayList<? extends Number>();
Run Code Online (Sandbox Code Playgroud)

Pau*_*ora 25

使用通配符实例化泛型类型是无效的语法.类型List<? extends Number>List某种类型的,其是或延伸Number.创建这种类型的实例没有意义,因为在实例化时你要创建特定的东西:

new ArrayList<? extends Number>();//compiler:"Wait, what am I creating exactly?" 
Run Code Online (Sandbox Code Playgroud)

具有通配符的泛型类型仅对变量和方法参数有意义,因为这样可以更自由地分配/传递给它们.

//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");
    }
}
Run Code Online (Sandbox Code Playgroud)

请务必记住使用通配符时出现的限制.

List<? extends Number> numList = ...
numList.add(new Integer(3));//compiler:"Nope, cause that might be a List<Double>"
Run Code Online (Sandbox Code Playgroud)

至于您的第一个示例,菱形是Java 7中的一项新功能,它允许编译器根据分配给它的变量的类型推断新通用实例的类型.在这种情况下:

List<? extends Number> x = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)

编译器很可能new ArrayList<Number>()在这里推断,但推断的内容几乎不重要,只要它是对给定变量的有效赋值.这就是引入菱形运算符的原因 - 指定新对象的泛型类型是多余的,因为一些泛型类型会使它成为有效的赋值/参数.

如果您记得Java中的泛型是纯粹的编译时语言特性,因为类型擦除,并且在运行时没有意义,这种推理才有意义.通配符只是因为这种限制而存在.相比之下,在C#泛型类型信息在运行时粘附 - 并且该语言中不存在通用通配符.