请解释这个通用代码通配符编译时间错误:
//no compile time error. List<? extends Number> x = new ArrayList<>(); //compile time error. List<? extends Number> x = new ArrayList<? 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<>();
编译器很可能在这里推断出新的ArrayList< Number>(),但是推断几乎不重要,只要它是给定变量的有效赋值.这就是引入钻石运算符的原因 – 指定新对象的通用类型是多余的,只要一些通用类型将使其成为有效的赋值/参数.
如果您记住Java中的泛型是纯编译时语言功能,因为type erasure,并且在运行时没有意义,这种推理才有意义.通配符只因为这个限制而存在.相比之下,在C#泛型类型的信息在运行时附近 – 通用通配符不存在于该语言.