Java 7/8泛型中嵌套通配符的可赋值性差异

Dao*_*Wen 8 java generics java-8 bounded-wildcard

以下在JDK8中编译得很好,但是与JDK7 给出了不兼容的类型错误.

List<List<? extends Number>> xs = Arrays.asList(Arrays.asList(0));
Run Code Online (Sandbox Code Playgroud)

根据这个答案,List<List<? extends Number>>没有超类型的关系List<List<Integer>>.

在Java 8中改变了什么使得这个任务有效?我也有一个很难理解为什么它不会在Java 7的工作.


这两个语句使用JDK7编译时没有类型错误:

List<? extends Number> xs = Arrays.asList(0);
List<? extends List<? extends Number>> ys = Arrays.asList(Arrays.asList(0));
Run Code Online (Sandbox Code Playgroud)

对我来说,这两个都在JDK7中工作似乎非常不直观,但上面的原始示例却没有.所有这些当然都适用于JDK8.我想要真正理解这里发生了什么,我需要理解为什么这些例子在Java 7中是合法的,但原始的例子却不是.

Sot*_*lis 9

我相信这与调用上下文扩展引用转换有关.

基本上,在该调用上下文中,参数的类型0Arrays.asList(0)可盒装到Integer,然后加宽到Number.当发生这种情况时,Arrays.asList(0)返回类型为List<Number>.通过相同的过程,List<Number>可以在将List<? extends Number>其用作外部参数之前进行转换Arrays.asList(..).

这相当于在Java 7中使用显式类型参数

List<List<? extends Number>> xs = Arrays.<List<? extends Number>>asList(Arrays.<Number>asList(0));
Run Code Online (Sandbox Code Playgroud)

在Java 7中,表达式的类型无论在何处使用都是相同的,无论是独立表达式还是在赋值表达式中使用.

Java 8引入了多表达式,其中表达式的类型可能受表达式的目标类型的影响.

例如,在以下赋值表达式中

List<Number> numbers = Arrays.asList(1);
Run Code Online (Sandbox Code Playgroud)

表达式Arrays.asList(1)的类型是被调用方法的返回类型,它完全取决于泛型类型参数.在这种情况下,将推断该类型参数,Integer因为该值1可以Integer通过装箱转换转换为(原语不能与泛型一起使用).所以表达式的类型是List<Integer>.

在Java 7中,此赋值表达式无法编译,因为List<Integer>无法分配List<Number>.这可以通过在调用时提供显式类型参数来解决asList

List<Number> numbers = Arrays.<Number>asList(1);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,方法调用需要一个Number参数作为其第一个参数,并且该值1满足该参数.

在Java 8中,赋值表达式是poly表达式

如果满足以下所有条件,则方法调用表达式是poly表达式:

  • 调用出现在赋值上下文或调用上下文中(第5.2节,第5.3节).

  • 如果调用是合格的(即MethodInvocation除了第一个之外的任何形式),则调用TypeArguments省略到标识符的左侧.

  • 由以下小节确定的要调用的方法是通用的(第8.4.4节),并且具有提及方法的类型参数中的至少一个的返回类型.

作为多义表达式,它可以受到它所分配的变量类型的影响.这就是发生的事情.泛型类型Number影响调用中推断的类型参数Arrays.asList(1).

请注意它在以下示例中不起作用

List<Number> numbers = ...;
List<Integer> integers = ...; // integers is not a poly expression
numbers = integers; // nope
Run Code Online (Sandbox Code Playgroud)

所以它不是协方差,但我们在某些地方获得了一些好处.

  • @ user3580294我最近阅读了Angelika Langer关于调用上下文的文章的一部分(尽管我认为它是在Java 8中应用之前).我记得那个词而且只是`ctrl + f`'ed. (2认同)