Java 7/8泛型中可分配性与嵌套通配符的区别 [英] Difference of assignability with nested wildcards in Java 7/8 generics

查看:90
本文介绍了Java 7/8泛型中可分配性与嵌套通配符的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码在JDK8中编译得很好,但是给JDK7一个不兼容的类型错误。

 列表与LT;名单,LT ;?扩展Number>> xs = Arrays.asList(Arrays.asList(0)); 

根据这个答案列表<列表< ;?
>

在Java 8中做了这项分配的工作有什么变化?我也很难理解为什么它不在Java 7中工作。






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

  List< ;?扩展Number> xs = Arrays.asList(0); 
列表< ;?扩展List <?扩展Number>> ys = Arrays.asList(Arrays.asList(0));

对于我来说,这两个工作在JDK7中似乎都很不直观,但上面的原始示例并不。他们当然都可以在JDK8上运行。我认为要真正理解这里发生了什么,我需要理解为什么这些示例在Java 7中是合法的,但最初的例子不是。 解决方案 div>

我相信这与调用上下文 0 Arrays.asList(0)可以被装箱到整数然后加宽为编号。发生这种情况时, Arrays.asList(0)的返回类型为 List< Number> 。通过相同的过程,可以将 List< Number> 转换为 List<在用作外部 Arrays.asList(..)的参数之前,扩展Number>



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

  List< List<扩展Number>> xs =阵列。<列表<扩展Number>> asList(Arrays。< Number> asList(0)); 






在Java 7中,表达式的类型是不管它在哪里使用,它是独立表达式还是用于赋值表达式。



Java 8引入 poly-expressions ,其中表达式的目标类型可能会影响表达式的类型。 / p>

例如,在下面的赋值表达式中:

  List< Number> numbers = Arrays.asList(1); 

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

在Java 7中,这个赋值表达式不会被编译,因为 List< Integer> 不能被赋值给列表与LT;号> 。当调用 asList

 时,通过提供显式类型参数可以解决这个问题。列表与LT;号> numbers = Arrays。< Number> asList(1); 

在这种情况下,方法调用需要一个 Number >参数的第一个参数,并且值 1 满足这个要求。



在Java 8中,赋值表达式是多语言表达 p>


如果以下所有
都为真,则方法调用表达式是一个poly表达式:




  • 调用显示在赋值上下文或调用上下文中(§5.2,§5.3)。


  • 如果调用是限定的(即,除第一个以外,任何形式的 MethodInvocation ),那么调用elides TypeArguments 标识符左侧

  • 要调用的方法,根据以下小节的规定,它是通用的(第8.4.4节)并且有返回n类型,至少提到一个方法的类型参数。





<作为一个poly表达式,它可能受到它被分配给的变量类型的影响。那就是发生了什么。泛型类型 Number 会影响在调用 Arrays.asList(1)时推断出的类型参数。



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

 列表与LT;号>数字= ...; 
列表<整数>整数= ...; //整数不是一个多元表达式
numbers =整数; // nope

所以这不是协方差,但我们在某些地方获得了一些好处。 p>

The following compiles just fine in JDK8, but gives an incompatible types error with JDK7.

List<List<? extends Number>> xs = Arrays.asList(Arrays.asList(0));

According to this answer, List<List<? extends Number>> doesn't have a supertype relationship to List<List<Integer>>.

What changed in Java 8 that made this assignment work? I'm also having a hard time understanding why it doesn't work in Java 7.


Both of these statements compile without type error using JDK7:

List<? extends Number> xs = Arrays.asList(0);
List<? extends List<? extends Number>> ys = Arrays.asList(Arrays.asList(0));

It seems very unintuitive to me that both of those work in JDK7, but the original example above does not. All of them of course will work in JDK8. I think to really understand what's going on here I'd need to understand why these examples are legal in Java 7 but the original example is not.

解决方案

I believe this has to do with invocation contexts and widening reference conversion.

Basically, in that invocation context, the type of the argument 0 in Arrays.asList(0) can be boxed to Integer and then widened to Number. When that happens, Arrays.asList(0) has a return type of List<Number>. Through the same process, that List<Number> can be converted to List<? extends Number> before being used as an argument to the outer Arrays.asList(..).

This is equivalent to using explicit type arguments in Java 7

List<List<? extends Number>> xs = Arrays.<List<? extends Number>>asList(Arrays.<Number>asList(0));


In Java 7, the type of an expression is the same regardless of where it is used, whether it's a standalone expression or used in assignment expression.

Java 8 introduced poly-expressions in which the type of the expression could be influenced by the target type of the expression.

For example, in the following assignment expression

List<Number> numbers = Arrays.asList(1);

The type of the expression Arrays.asList(1) is the return type of the method being invoked which depends entirely on the generic type parameter. In this case, that type argument will be inferred as Integer because the value 1 can be converted to Integer through boxing conversion (primitives can't be used with generics). So the type of the expression is List<Integer>.

In Java 7, this assignment expression would not compile because List<Integer> cannot be assigned to List<Number>. This could be fixed by providing an explicit type argument when invoking asList

List<Number> numbers = Arrays.<Number>asList(1);

in which case, the method invocation expects a Number argument for its first parameter and the value 1 satisfies that.

In Java 8, the assignment expression is a poly expression

A method invocation expression is a poly expression if all of the following are true:

  • The invocation appears in an assignment context or an invocation context (§5.2, §5.3).

  • If the invocation is qualified (that is, any form of MethodInvocation except for the first), then the invocation elides TypeArguments to the left of the Identifier.

  • The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a return type that mentions at least one of the method's type parameters.

Being a poly expression, it can be influenced by the type of the variable it is being assigned to. And that's what happens. The generic type Number influences the type argument inferred in the invocation of Arrays.asList(1).

Note how it wouldn't work in the following example

List<Number> numbers = ...;
List<Integer> integers = ...; // integers is not a poly expression
numbers = integers; // nope

So it's not covariance, but we get some of its benefits in some places.

这篇关于Java 7/8泛型中可分配性与嵌套通配符的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆