为什么这个Java 8程序不能编译?

2022-08-31 13:04:00

该程序在 Java 7 中编译良好(或在 Java 8 中使用 ),但无法使用 Java 8 编译:-source 7

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());
    }
}

结果:

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

换句话说,这是 Java 7 和 8 之间的向后源代码不兼容。我已经经历了Java SE 8和Java SE 7列表之间的不兼容性,但没有找到任何适合我的问题的东西。

那么,这是一个错误吗?

环境:

$ /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)

答案 1

Java语言规范在类型推断方面发生了重大变化。在 JLS7 中,类型推断在 §15.12.2.7§15.12.2.8 中进行了描述,而在 JLS8 中,有一整章专门介绍第 18 章。键入推理

JLS7 和 JLS8 中的规则都非常复杂。很难说出差异,但显然存在差异,从§18.5.2节中可以明显看出:

此推理策略与 Java SE 7 版 Java 语言规范 [..] 不同。

但是,更改的意图是向后兼容。见§18.5.2节的最后一段:

[..]该策略允许在典型用例中获得合理的结果,并且向后兼容 Java SE 7 版 Java 语言规范中的算法。

我不知道这是不是真的。但是,您的代码有一些有趣的变体,它们不会显示问题。例如,以下语句编译时不会出现错误:

new Acceptor<>(new Impl());

在这种情况下,没有目标类型。这意味着类实例创建表达式不是多边形表达式,并且类型推断的规则更简单。见§18.5.2

如果调用不是多元表达式,则让绑定集 B3 与 B2 相同。

这也是为什么以下陈述有效的原因。

Acceptor<?> acceptor = (Acceptor<?>) new Acceptor<>(new Impl());

尽管表达式的上下文中有一个类型,但它不算作目标类型。如果类实例创建表达式赋值表达式调用表达式中均未发生,则它不能是 poly 表达式。见§15.9

如果类实例创建表达式使用菱形形式作为类的类型参数,并且它出现在赋值上下文或调用上下文中(§5.2,§5.3),则该表达式是 poly 表达式 (§15.2)。否则,它是一个独立的表达式。

回到你的发言。JLS8 的相关部分同样是 §18.5.2。但是,根据JLS8,我无法告诉您以下语句是否正确,或者编译器是否正确,并且错误消息是正确的。但至少,您有一些替代方案和指针以获取更多信息。

Acceptor<?> acceptor = new Acceptor<>(new Impl());

答案 2

感谢您的报告。这看起来像一个错误。我会处理它,一旦我们有更多关于为什么会发生这种情况的信息,我可能会添加一个更好的答案。我已经提交了这个错误条目JDK-8043926,以跟踪它。


推荐