【问题标题】:Why does the following code crash javac? What can be done about it?为什么下面的代码会导致 javac 崩溃?可以做些什么呢?
【发布时间】:2015-12-12 05:11:32
【问题描述】:

我在阅读 this article 关于“Java 中的奇怪事物”的内容时遇到了一个有趣的概念:不可确定的类型。

考虑以下三个类/接口:

public interface Type<T> { }
public class D<P> implements Type<Type<? super D<D<P>>>> { }
public class WildcardTest {
  Type<? super D<Byte>> d = new D<Byte>();
}

显然问题在于D 是否是Type&lt;? super D&lt;Byte&gt;&gt; 是无法确定的;谁能进一步解释一下?

javac 1.8.0_60 在尝试编译 WildcardTest 时抛出一个很长的 StackOverflowError

The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3834)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)

此代码还会使整个 Eclipse IDE 崩溃。

作者提交了一个错误with the Eclipse team,但没有得到任何投票(除了我的)。有什么可以做的吗?这只是编译器形式的Halting Problem 吗?

There is also a link to this paper about it in the article, but I am hoping there is a more straightforward explanation.

【问题讨论】:

  • 显然,它是a known bug(从 2007 年开始!)计划在 Java 9 中修复。
  • 我本来打算参考“驯服通配符”,但它已经被文章链接了,我认为我们没有人能比论文更好地解释。另外,论文没有说它是否是不可判定的。
  • @durron597 - 我不能假装我理解这个问题:) 我能理解这个困难。想想一个更简单的问题——supertypes of Integer 是什么?
  • JLS 通过直接超类型定义子类型。但我不清楚直接超类型集是如何定义的,当它是递归的时候。
  • @Powerlord - 这正是通配符通常使用的地方:) - 作为变量类型。 (当你调用带有通配符参数的方法时,相当于将参数分配给参数)

标签: java generics types compilation stack-overflow


【解决方案1】:

正如 Tunaki 在 cmets 中指出的那样,这可以追溯到 Pierce(TAPL 作者)合着的 Microsoft research paper。事实上,Tate 等人的问题。给出的是附录 A 中的示例 2(带有 Byte = TType = ND = C)。

堆栈溢出

首先,让我们弄清楚为什么它会破坏堆栈。为此,最好提醒自己编译器检查类型的方式几乎与我们相同。我们遇到它就会遇到的问题。

// To be determined:
D<Byte> <: Type<? super D<Byte>>

// using D<P> implements Type<Type<? super D<D<P>>>>

Type<Type<? super D<D<Byte>>>> <: Type<? super D<Byte>>

// The outermost type constructor (Type) matches. For the
// suptyping relationship to hold, we have to test the type
// arguments. (Sides are flipped due to contravariance.)

D<Byte> <: Type<? super D<D<Byte>>>

// Mhh… That looks an awful lot like above.
// Feel free to rinse and repeat until your "stack" blows too… ;)

皮尔斯等人。在他们论文的第 3 节中描述了与示例 2 类似的回归。略有不同,因为 Java 不允许类型变量的下限,只有通配符。

可以做什么?

类似于 Pierce 等人给出的示例 1,此回归遵循一个模式。编译器可以检测到这种模式,并且可以假设子类型关系在共归纳解释下成立。 (这与 F 有界多态性的推理相同,即Enum&lt;E extends Enum&lt;E&gt;&gt;。您进入了类似的无限回归,没有矛盾。)

无法确定?

给定的问题可以通过更智能的算法来解决。 Java 的类型系统是否可判定仍然是一个悬而未决的问题。

如果 Java 允许类型参数的下限,它将是不可判定的(半可判定的)。然而,在他们的论文 Pierce 等人。定义可以使这种类型系统再次可判定的限制。顺便说一句,Scala 采用了这些限制,具有讽刺意味的是,它具有图灵完备的类型系统。

【讨论】:

    猜你喜欢
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-10-05
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多