[英]Why is this invalid Java? Type of ternary operator output
看看这段代码。
// 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
(i2 == null) ? i1 : i2
是i1
和i2
的共同祖先,它是Object。 for
语句要求表达式的静态类型为Iterable
或数组类型。 情况并非如此,因此您会收到编译错误。
现在,如果你问为什么编译器没有推导出(i2 == null) ? i1 : i2
(i2 == null) ? i1 : i2
将始终是一个数组或一个Iterable:
假设,如果Java类型系统有点不同,则可以更好地处理这种特殊情况。 具体来说,如果Java支持代数数据类型,则可以将o
声明为“对象数组或可迭代”......并且for
循环可以是类型可检查的。
1 - 假设o
已被初始化为o = (x * x < 0) ? new Object() : new Object[0]
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)
你真的要求编译器在Object上生成一个foreach循环,这是非法的。
在这种情况下,条件表达式具有两种类型的最小上限类型,即Object
,而foreach循环不适用于Object
类型
你真的应该添加两个重载的deepPrint(Object [])和deepPrint(Iterator i)方法,并在你的deepPrint(Object对象)中进行调度。 是的,因为for / each循环的工作原理,您需要复制并粘贴相同的代码并进行微小的更改。
试图把所有这些都放到一个大方法中是一种气味。
您可以通过在Arrays.asList(Object [])中包装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的答案,它对于编译器编写者来说更简单,但它可以说是一种更简单的语言设计)三元运算符假定这种类型是两种返回类型之间最常见的标记,而不是容纳一种类型。两种返回类型。 考虑方法重载的情况。 确定正确的方法并编译时间。 这意味着编译器必须能够在编译时对声明的返回类型做出决定,并且不能将该决定延迟到运行时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.