为什么这个 Java 8 程序不能编译?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23063474/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does this Java 8 program not compile?
提问by ghik
This program compiles fine in Java 7 (or in Java 8 with -source 7
), but fails to compile with Java 8:
这个程序在 Java 7(或在 Java 8 with -source 7
)中编译得很好,但在 Java 8中编译失败:
interface Iface<T> {}
class Impl implements Iface<Impl> {}
class Acceptor<T extends Iface<T>> {
public Acceptor(T obj) {}
}
public class Main {
public static void main(String[] args) {
Acceptor<?> acceptor = new Acceptor<>(new Impl());
}
}
Result:
结果:
Main.java:10: error: incompatible types: cannot infer type arguments for Acceptor<>
Acceptor<?> acceptor = new Acceptor<>(new Impl());
^
reason: inference variable T has incompatible bounds
equality constraints: Impl
upper bounds: Iface<CAP#1>,Iface<T>
where T is a type-variable:
T extends Iface<T> declared in class Acceptor
where CAP#1 is a fresh type-variable:
CAP#1 extends Iface<CAP#1> from capture of ?
1 error
In other words, this is a backwardssource incompatibility between Java 7 and 8. I've gone through Incompatibilities between Java SE 8 and Java SE 7list but did not found anything that would fit my problem.
换句话说,这是Java 7 和 8 之间的向后源不兼容性。我已经浏览了Java SE 8 和 Java SE 7列表之间的不兼容性,但没有找到适合我的问题的任何内容。
So, is this a bug?
那么,这是一个错误吗?
Environment:
环境:
$ /usr/lib/jvm/java-8-oracle/bin/java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
采纳答案by Vicente Romero
Thanks for the report. This looks like a bug. I will take care of it and probably add a better answer once we have more information about why this is happening. I have filed this bug entry JDK-8043926, to track it.
谢谢你的报告。这看起来像一个错误。我会处理它,一旦我们有更多关于为什么会发生这种情况的信息,我可能会添加一个更好的答案。我已提交此错误条目JDK-8043926以对其进行跟踪。
回答by nosid
The Java Language Specification changed significantly regarding type inference. In JLS7, type inferencewas described in §15.12.2.7and §15.12.2.8, whereas in JLS8, there is a whole chapter dedicated to Chapter 18. Type Inference.
Java 语言规范在类型推断方面发生了重大变化。在 JLS7 中,类型推断在§15.12.2.7和§15.12.2.8 中进行了描述,而在 JLS8 中,有一整章专门介绍第 18 章类型推断。
The rules are quite complex, both in JLS7 and JLS8. It's difficult to tell the differences, but obviously there are differences, as is evident from section §18.5.2:
JLS7 和 JLS8 中的规则都非常复杂。很难区分差异,但显然存在差异,从第§18.5.2节可以明显看出:
This inference strategy is different than the Java SE 7 Edition of The Java Language Specification [..].
这种推理策略与 Java 语言规范 [..] 的 Java SE 7 版不同。
However, the intention of the change was to be backwards compatible. See the last paragraph of section §18.5.2:
但是,更改的目的是向后兼容。见第18.5.2节的最后一段:
[..] The strategy allows for reasonable outcomes in typical use cases, and is backwards compatible with the algorithm in the Java SE 7 Edition of The Java Language Specification.
[..] 该策略允许在典型用例中产生合理的结果,并且与 Java 语言规范的 Java SE 7 版中的算法向后兼容。
I can't tell whether that's true or not. However, there are some interesting variations of your code, that do not show the problem. For example, the following statement compiles without errors:
我不知道这是真的还是假的。但是,您的代码有一些有趣的变体,并没有显示出问题。例如,以下语句编译没有错误:
new Acceptor<>(new Impl());
In this case, there is no target type. That means the Class Instance Create Expressionis not a poly expressions, and the rules for type inferenceare simpler. See §18.5.2:
在这种情况下,没有目标类型。这意味着Class Instance Create Expression不是一个poly 表达式,类型推断的规则更简单。见第18.5.2 节:
If the invocation is not a poly expression, let the bound set B3be the same as B2.
如果调用不是多边形表达式,则让绑定集 B 3与 B 2相同。
That's also the reason, why the following statement works.
这也是为什么以下语句有效的原因。
Acceptor<?> acceptor = (Acceptor<?>) new Acceptor<>(new Impl());
Although there is a type in the context of the expression, it doesn't count as target type. If a class instance creation expressiondoes not happen in either an assignment expressionor an invocation expression, then it can't be a poly expression. See §15.9:
尽管表达式的上下文中存在类型,但它不算作目标类型。如果类实例创建表达式没有出现在赋值表达式或调用表达式中,那么它就不能是多表达式。见第15.9 节:
A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). Otherwise, it is a standalone expression.
如果类实例创建表达式使用菱形形式作为类的类型参数,并且出现在赋值上下文或调用上下文(第 5.2 节,第 5.3 节)中,则该表达式是一个多边形表达式(第 15.2 节)。否则,它是一个独立的表达式。
Coming back to your statement. The relevant part of the JLS8 is again §18.5.2. However, I can't tell you if the following statement is correct according to the JLS8, of if the compiler is right with the error message. But at least, you got some alternatives and pointers for further information.
回到你的陈述。JLS8 的相关部分再次是§18.5.2。但是,根据 JLS8,我无法告诉您以下语句是否正确,或者编译器是否正确处理错误消息。但至少,您可以获得一些替代方案和指示以获取更多信息。
Acceptor<?> acceptor = new Acceptor<>(new Impl());
回答by Aleksandr Dubinsky
Type inference was changed in Java 8. Now, type inference looks at both the target type and the parameter types, for both constructors and methods. Consider the following:
Java 8 中的类型推断发生了变化。现在,类型推断会同时查看目标类型和参数类型,包括构造函数和方法。考虑以下:
interface Iface {}
class Impl implements Iface {}
class Impl2 extends Impl {}
class Acceptor<T> {
public Acceptor(T obj) {}
}
<T> T foo(T a) { return a; }
The following is now ok in Java 8 (but not in Java 7):
以下现在在 Java 8 中是可以的(但在 Java 7 中不是):
Acceptor<Impl> a = new Acceptor<>(new Impl2());
// Java 8 cleverly infers Acceptor<Impl>
// While Java 7 infers Acceptor<Impl2> (causing an error)
This, of course, gives an error in both:
当然,这在两个方面都会产生错误:
Acceptor<Impl> a = new Acceptor<Impl2>(new Impl2());
This also is ok in Java 8:
这在 Java 8 中也可以:
Acceptor<Impl> a = foo (new Acceptor<>(new Impl2()));
// Java 8 infers Acceptor<Impl> even in this case
// While Java 7, again, infers Acceptor<Impl2>
// and gives: incompatible types: Acceptor<Impl2> cannot be converted to Acceptor<Impl>
The following gives an error in both, but the error is different:
以下给出了两者的错误,但错误是不同的:
Acceptor<Impl> a = foo (new Acceptor<Impl2>(new Impl2()));
// Java 7:
// incompatible types: Acceptor<Impl2> cannot be converted to Acceptor<Impl>
// Java 8:
// incompatible types: inferred type does not conform to upper bound(s)
// inferred: Acceptor<Impl2>
// upper bound(s): Acceptor<Impl>,java.lang.Object
Clearly, Java 8 made the type inference system smarter. Does this cause incompatibilities? Generally, no. Because of type erasure, it doesn't actually matter what types were inferred, as long as the program compiles. Does Java 8 compile all Java 7 programs? It should, but you brought up a case where it does not.
显然,Java 8 使类型推断系统更加智能。这会导致不兼容吗?一般来说,没有。由于类型擦除,只要程序编译,推断出什么类型实际上并不重要。Java 8 是否编译所有 Java 7 程序?它应该,但你提出了一个没有的案例。
What seems to be happening is that Java 8 is not handling wildcards well. Instead of considering them as a lack of a constraint, it seems to be treating them as a restrictive constraint that it can't satisfy. I'm not sure if it's following the letter of the JLS, but I'd call this a bug at least in spirit.
似乎正在发生的事情是 Java 8 没有很好地处理通配符。它没有将它们视为缺乏约束,而是将它们视为它无法满足的限制性约束。我不确定它是否遵循 JLS 的信函,但至少在精神上我认为这是一个错误。
FYI, this does work (note that my Acceptor
doesn't have the type constraints that yours does):
仅供参考,这确实有效(请注意,我的Acceptor
没有您的类型限制):
Acceptor<?> a = new Acceptor<>(new Impl2());
Do note that your example is using a wildcard type outside of a method parameter (which is inadvisable), I wonder if the same issue will happen in more typical code that uses the diamond operator in method calls. (Probably.)
请注意,您的示例在方法参数之外使用了通配符类型(这是不可取的),我想知道在方法调用中使用菱形运算符的更典型的代码中是否会发生同样的问题。(大概。)