为什么这个Java无效?三元运算符输出的类型

And*_*lla 2 java compiler-construction iterator ternary-operator

看看这段代码.

// Print object and recurse if iterable
private static void deep_print(Object o) {
  System.out.println(o.getClass().toString() + ", " + o.toString());

  boolean iter = false;
  Iterable<?> i1 = null;
  Object[] i2 = null;

  if (o instanceof Iterable<?>) {
    iter = true;
    i1 = (Iterable<?>) o;
  } else if (o instanceof Object[]) {
    iter = true;
    i2 = (Object[]) o;
  }

  if (iter) {
    for (Object o_ : i2 == null ? i1 : i2) deep_print(o_); // ERROR: Can only iterate over an array or an instance of java.lang.Iterable
  }
Run Code Online (Sandbox Code Playgroud)

我知道如何解决它.我只想知道它为什么会发生.编译器不应该只检查所有可能的输出吗?

Ste*_*n C 8

表达的静态结果类型(i2 == null) ? i1 : i2是共同的祖先i1i2哪个是对象.甲for语句需要的静态类型表达的是任一种Iterable或阵列型.情况并非如此,因此您会收到编译错误.

现在,如果你问为什么编译器不会推断出(i2 == null) ? i1 : i2总是数组或Iterable:

  1. Java语言规范(JLS)不允许这样做.
  2. 如果JLS确实允许它(但不要求它),那么不同的编译器会表现出不同的行为,这取决于它们在定理证明方面有多好.坏.
  3. 如果JLS需要它,那么编译器必须包含复杂的定理证明器1(BAD SLOW),并且遇到解决停机问题2(BAD BAD BAD)的"轻微不便" .
  4. 实际上,编译器需要知道表达式具有哪两种类型,因为它需要在每种情况下生成不同的代码.

假设,如果Java类型系统有点不同,则可以更好地处理这种特殊情况.具体来说,如果Java支持代数数据类型,则可以将其声明o为"对象数组或可迭代"......并且for循环可以是类型可检查的.


1 - 假设o已初始化为o = (x * x < 0) ? new Object() : new Object[0].确定总是会导致Object[]实例的实例需要一个小证明,其中涉及(实数)的平方不是负数的事实.这是一个简单的例子,可以构造任意复杂的例子,需要任意的难以证明.

2 - 停止问题在数学上被证明是一个不可计算的函数.换句话说,存在这样的功能,即在数学上不可能证明它们是否终止.