查看此代码。
// 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
}
我知道怎么解决。我只是想知道为什么会这样。编译器不应该简单地检查所有可能的输出吗?
表达式
(i2 == null) ? i1 : i2
的静态结果类型是i1
和i2
的共同祖先,即Object。 for
语句要求表达式的 静态类型 为 Iterable
或数组类型。 事实并非如此,因此您会收到编译错误。
现在,如果您问为什么编译器不推断出
(i2 == null) ? i1 : i2
将始终是数组或 Iterable:
假设,如果 Java 类型系统有点不同,这种特殊情况可以得到更好的处理。 具体来说,如果 Java 支持 代数数据类型 ,那么可以将
o
声明为“对象数组或可迭代对象”...并且 for
循环将是可类型检查的。
1 - 假设
o
已初始化为 o = (x * x < 0) ? new Object() : new Object[0]
。 确定这总是会导致 Object[]
实例需要一个小证明,涉及以下事实:(实)数的平方不为负。 这是一个简单的例子,可以构造任意复杂的例子,需要任意困难的证明。
2 - 停止问题在数学上被证明是一个不可计算的函数。 换句话说,存在一些函数,在数学上无法证明它们是否终止。
为了说明 Stephen C 的答案,请考虑以下代码:
void test() {
Iterable<Integer> i1 = new ArrayList<Integer>();
Object[] i2 = { 1, 2, 3 };
method1(false ? i1 : i2);
method1(true ? i1 : i2);
}
void method1(Object o) {
System.out.println("method1(Object) called");
}
void method1(Object[] o) {
System.out.println("method1(Object[]) called");
}
void method1(Iterable<?> o) {
System.out.println("method1(Iterable<?>) called");
}
这是 test() 的输出:
method1(Object) called
method1(Object) called
由于方法重载是在编译时完成的,所以可以看到三元运算符表达式的静态类型是Object,因为操作数的类型不同。因此,当您这样做时:
for (Object o_ : i2 == null ? i1 : i2)
您实际上是在要求编译器在对象上生成 foreach 循环,这是非法的。
在这种情况下,条件表达式具有两种类型中最小上限的类型,即
Object
,foreach循环对类型Object
不起作用
您确实应该添加两个重载的 deepPrint( Object[] ) 和 deepPrint(Iterator i) 方法,并在 deepPrint(Object object) 中进行调度。是的,由于 for/each 循环工作方式的本质,您将需要复制并粘贴相同的代码并进行细微的更改。
尝试将所有这些放入一个大方法中是一种味道。
您可以通过将 Object[] 包装在 Arrays.asList(Object[]) 中来避免代码重复,然后在所有情况下都拥有一个 Iterable。是的,它比在数组上工作稍慢,但它具有 DRY 的优点,这应该始终是第一个近似值,恕我直言。
所以你最终会得到这样的结果:
Iterable<?> it = null;
if (o instanceof Iterable<?>) {
it = (Iterable<?>) o;
} else if (o instanceof Object[]) {
it = Arrays.asList((Object[]) o);
}
if (it != null) {
for (Object o_ : it) deep_print(o_);
}
为了简单起见(请参阅 Stephen C 的回答,了解它对于编译器编写者来说如何更简单,但它也可以说是一种更简单的语言设计)三元运算符假定类型是两个返回类型之间的最低共同点,而不是容纳两种返回类型之一。考虑方法重载的情况。正确的方法是由编译时间决定的。这意味着编译器必须能够在编译时就声明的返回类型做出决定,并且不能将该决定延迟到运行时。