为什么这在 Java7 中编译而在 Java8 中不编译? [英] Why does this compile in Java7 and does not in Java8?

查看:26
本文介绍了为什么这在 Java7 中编译而在 Java8 中不编译?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

泛型很棘手.看起来它们在不同版本的 Java 中被区别对待.

Generics are tricky. And looks like they are treated differently in different versions of Java.

此代码在 Java 7 中成功编译,但在 Java 8 中无法编译.

This code successfully compiles in Java 7 and fails to compile with Java 8.

import java.util.EnumSet;

public class Main {
  public static void main(String[] args) {
    Enum foo = null;
    tryCompile(EnumSet.of(foo));
  }

  static <C extends Enum<C> & Another> void tryCompile(Iterable<C> i) {}

  static interface Another {}
}

这是来自 Java 8 的错误消息.我用这个来编译它:http://www.compilejava.net/

Here is an error message from Java 8. I used this one to compile it: http://www.compilejava.net/

/tmp/java_A7GNRg/Main.java:6: error: method tryCompile in class Main cannot be applied to given types;
    tryCompile(EnumSet.of(foo));
    ^
  required: Iterable<C>
  found: EnumSet
  reason: inferred type does not conform to upper bound(s)
    inferred: Enum
    upper bound(s): Enum<Enum>,Another
  where C is a type-variable:
    C extends Enum<C>,Another declared in method <C>tryCompile(Iterable<C>)
/tmp/java_A7GNRg/Main.java:6: warning: [unchecked] unchecked method invocation: method of in class EnumSet is applied to given types
    tryCompile(EnumSet.of(foo));
                         ^
  required: E
  found: Enum
  where E is a type-variable:
    E extends Enum<E> declared in method <E>of(E)
1 error
1 warning

问题是关于Java编译器版本之间的差异.

The question is about the difference between versions of Java compiler.

推荐答案

Java 7 和 Java 8 之间的主要区别在于目标类型推断.虽然 Java 7 只考虑方法调用的参数来确定类型参数,但 Java 8 将使用表达式的目标类型,即嵌套方法调用时的参数类型、初始化或分配的变量的类型to,或者在 return 语句的情况下方法的返回类型.

The main difference between Java 7 and Java 8 is the target type inference. While Java 7 only considers the parameters of a method invocation to determine the type arguments, Java 8 will use the target type of an expression, i.e. the parameter type in case of a nested method invocation, the type of the variable that is initialized or assigned to, or the method’s return type in case of a return statement.

例如编写时,Listlist=Arrays.asList(1, 2, 3, 4);,Java 7 将通过查看方法的参数推断右侧的类型 List一个错误,而 Java 8 将使用目标类型 List 来推断方法参数必须是 Number 的实例的约束,就是这种情况.因此,它在 Java 8 中是合法的.

E.g. when writing, List<Number> list=Arrays.asList(1, 2, 3, 4);, Java 7 will infer the type List<Integer> for the right hand side by looking at the method’s arguments and generate an error while Java 8 will use the target type List<Number> to infer the constraint that the method arguments must be instances of Number which is the case. Therefore, it is legal in Java 8.

如果对正式的细节感兴趣,可以研究Java 语言规范,第 18 章.类型推断",尤其是 §18.5.2.调用类型推断,然而,这并不容易阅读......

If you are interested in the formal details, you may study the "Java Language Specification, Chapter 18. Type Inference", especially §18.5.2. Invocation Type Inference, however, that’s not easy reading…

那么当你说 Enum foo = null; 时会发生什么?tryCompile(EnumSet.of(foo));?

在 Java 7 中,表达式 EnumSet.of(foo) 的类型将通过查看参数的类型来推断,foo 是原始类型 Enum,因此将执行未经检查的操作,结果类型为原始类型 EnumSet.这种类型实现了原始类型 Iterable,因此可以传递给 tryCompile 形成另一个未经检查的操作.

In Java 7 the type of the expression EnumSet.of(foo) will be inferred by looking at the type of the argument, foo which is the raw type Enum, hence an unchecked operation will be performed and the result type is the raw type EnumSet. This type implements the raw type Iterable and hence can be passed to tryCompile forming another unchecked operation.

在 Java 8 中,EnumSet.of(foo) 的目标类型是 tryCompile 的第一个参数的类型,即 Iterable&另一个>,所以不用过多赘述,在 Java 7 EnumSet.of 中将被视为原始类型调用,因为它具有原始类型参数,在 Java 8 中将被视为作为泛型调用,因为它具有泛型目标类型.通过将其视为通用调用,编译器将得出结论,找到的类型 (Enum) 与所需的类型 C extends Enum 不兼容.&另一个.虽然您可以将原始类型 Enum 分配给 C extends Enum 并带有未经检查的警告,但它会被认为与 Another(没有类型转换).

In Java 8 the target type of EnumSet.of(foo) is the type of the first parameter of tryCompile which is Iterable<C extends Enum<C> & Another>, so without going too much into details, in Java 7 EnumSet.of will be treated as raw type invocation because it has a raw type argument, in Java 8 it will be treated as generic invocation because it has a generic target type. By treating it as as a generic invocation, the compiler will conclude that the type found (Enum) is not compatible to the required type C extends Enum<C> & Another. While you could get away with assigning the raw type Enum to C extends Enum<C> with an unchecked warning, it will considered to be incompatible with Another (without a type-cast).

你确实可以插入这样的演员表:

You can indeed insert such a cast:

Enum foo = null;
tryCompile(EnumSet.of((Enum&Another)foo));

由于将 Enum 分配给 C extends Enum,这当然会编译,当然不是没有未经检查的警告.

This compiles, of course not without an unchecked warning due to the assignment of Enum to C extends Enum<C>.

您还可以分解目标类型关系,以便执行与 Java 7 中相同的步骤:

You can also dissolve the target type relationship so that the same steps as in Java 7 are performed:

Enum foo = null;
EnumSet set = EnumSet.of(foo);
tryCompile(set);

在这里,三行都使用原始类型,因此编译时会出现未经检查的警告,并且与 Java 7 中的 implements Another 约束相同.

Here, raw types are used throughout the three lines so this compiles with unchecked warnings and the same ignorance about the implements Another constraint as in Java 7.

这篇关于为什么这在 Java7 中编译而在 Java8 中不编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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