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

2022-09-04 07:13:45

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

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

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

答案 1

使用通配符实例化泛型类型是无效的语法。该类型表示 某种类型的 a,该类型是 或 扩展 。创建此类型的实例没有意义,因为使用实例化,您将创建特定的东西:List<? extends Number>ListNumber

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# 中,泛型类型信息在运行时仍然存在 - 并且该语言中不存在泛型通配符。


答案 2

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

相反。


推荐