为什么这在 Java7 中编译而在 Java8 中不编译? [英] Why does this compile in Java7 and does not in 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.
例如编写时,List
,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屋!