【问题标题】:When does Java type inference produce an infinite type?Java 类型推断何时产生无限类型?
【发布时间】:2010-08-10 17:08:20
【问题描述】:

JLS 在类型推断算法(§15.12.2)中提到:

上面的过程有可能产生一个无限类型。这是允许的, Java 编译器必须识别这种情况并使用循环数据结构适当地表示它们。

但是,我找不到 javac 生成无限类型的实际示例。 我认为它应该在以下情况下产生一个:

<T> T pick(T a, T b) { ... }

pick("string", 3);

String 和 Integer 都是 Comparable,因此它们的共同超类型应该是 Comparable&lt;? extends Comparable&lt;? extends Comparable&lt;? ...&gt;&gt;&gt;(无限)。

我能做到:

Comparable<? extends Comparable<?>> x = pick("string", 3);

但后来我尝试了:

Comparable<? extends Comparable<? extends Comparable<?>>> x = pick("string", 3);

这不会编译。 似乎递归在 2 步后中止了。

你知道有什么情况可以让 Java 实际上产生一个无限类型吗?

--

编辑:上面似乎是一个编译器错误。阅读规范,让我们看看lub(String, Integer) 的计算结果如何:

ST(String) = { String, Comparable<String>, Serializable, CharSequence, Object }
ST(Integer) = { Integer, Comparable<Integer>, Serializable, Number, Object }
EC = { Comparable, Serializable, Object }
MEC = { Comparable, Serializable }
Inv(Comparable) = { Comparable<String>, Comparable<Integer> }
lcta(String, Integer) = ? extends lub(String, Integer)
lci(Inv(Comparable)) = Comparable<? extends lub(String, Integer)>
lub(String, Integer) = Serializable & Comparable<? extends lub(String, Integer)>

所以lub(String, Integer) 应该是一个无限类型。 Javac 在这里似乎是错误的。也许它毕竟没有实现无限类型?

【问题讨论】:

  • 请记住您引用的最后一部分:“...Java 编译器必须识别这种情况并适当地表示它们...”。如果你给它一个“无限类型”,任何符合规范的编译器都不会闪烁。将类型擦除添加到混合中,示例中的所有内容都会变成 Comparable 或 Object。因此,除非您正在构建 Java 编译器,否则您可能永远都不会注意到。
  • @cHao:看起来 Sun 的 javac 不符合规范。我无法用它产生任何无限类型,这就是我问这个问题的原因。似乎他们只是未实现此语言功能。

标签: java programming-languages wildcard type-inference


【解决方案1】:

以下代码将 javac 发送到无限循环。据推测,它试图构建一个无限类型,但没有设法将其表示为有限循环数据结构。

interface I<T> {}
interface A<T> extends I<A<A<T>>>{}
abstract class X {
    abstract <T> T foo(T x, T y);

    void bar(A<Integer> x, A<String> y){
        foo(x, y);
    }
}

【讨论】:

  • 我认为这个例子是一个“扩展继承”的例子。扩展继承涉及产生无限系列类型(每个都比前一个更大)的无限循环。但所涉及的每种类型都是有限的,因此它并不是问题的真正答案。 (我怀疑这个问题无法回答,因为 javac 实际上并没有实现无限类型)
【解决方案2】:

R.Grigore(2016) 在他的论文中回答了这个问题Java Generics are Turing Complete

他的建议以下面的Java代码为例:

//an empty interface
interface Z {}

//4 generic interfaces, one argument each
interface N<X> {}
interface L<X> {}
interface Qlr<X> {}
interface Qrl<X> {}

//one complex generic, inheriting from instantiations of two other
interface E<X> extends
    Qlr<N<? super Qr<? super E<? super E<? super X>>>>>,
    Qrl<N<?super Ql<? super E<? super E<? super X>>>>>
    {}

//main class with a single function
class Main{
    //heavily nested return type
    L<? super N<? super L<? super N<? super L<? super N<? super E<? super E<? super Z>>>>>>>>
    f(Qr<? super E<? super E<? super Z>>> v) {return v;}
}

【讨论】:

  • 那篇论文回答了一个不同的问题。图灵完备性涉及有限类型的无限大小。我的问题是关于“无限”(循环)类型。
【解决方案3】:

无限类型的可能性可能是(例如,地图内部地图的地图对象等......

Map<? extends Serializable, Map<? extends Serializable, Map<? extends Serializable, Map<? extends Serializable, Map<? extends Serializable, Map>>>>> objectMap;

(为了可读性,这样做到深度 5...但是您可以在无限深的地图中添加地图。

我不知道这是否是你要找的......

【讨论】:

  • 那么你如何产生这样的类型呢?它不能被命名(需要一个无限的源文件),所以它只能作为类型推断的结果出现。我正在寻找 Java 推断出这种无限类型的示例方法调用。
  • Reflection 将无法获取类型参数——它们在编译完成时就消失了。 “无限类型”仅在编译期间相关,而不是之前(类型推断生成它们,并或多或少透明地处理它们),而不是之后(类型擦除摆脱类型参数,将无限类型转换为普通类型像对象)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
相关资源
最近更新 更多