尝试以下方法:
class Base {
public Base(){
}
}
class Derived extends Base {
}
public class Main
{
public static void main (String[] args)
{
Derived derived = (Derived) new Base();
}
}
导致CastClassException
,但成功编译。对this question的评论表明,编译器无法确定new Base()
在运行时之前的类型 - 但为什么会出现这种情况呢?我知道在other type systems中,编译器总是可以推断出任何表达式的类型,并且Java编译器当然可以使用例如lambda表达式。在某些时候,如果它发出正确的字节码,它必须知道new Base()
的类型。
是否有理论上的限制阻止Java编译器在程序运行之前捕获它,或者它是一个实现选择?
答案 0 :(得分:5)
public static void main(String[] args) throws Exception {
Derived derived = (Derived) getObject();
}
public static Base getObject() {
if (Math.random() > 0.5) {
return new Base();
} else {
return new Derived();
}
}
现在怎么办?编译器如何知道?编译器(大部分)仅限于静态信息。对象的类型是动态信息。
答案 1 :(得分:3)
允许子类化的语言的任何合理实现都有可能出现强制转换异常(或其等价物)。
子类化方案的强大之处在于,您可以拥有在通用超类上运行的方法,而无需知道特定的子类。但是迟早会需要对子类进行特定的操作,并且必须将引用强制转换为子类。如果该转换是错误的,因为你误认为对象的实际类,那么逻辑上应该发生转换异常。
您可以隐藏某些机制背后的强制转换异常,例如,如果对象不是正确的类型,则返回NULL,但这只是将一个异常替换为另一个异常。
答案 2 :(得分:1)
new Base()
的结果是Base
,在任何其他上下文中可能是Derived.
编译器不会传播其中的特殊知识这种情况确实是Base.
是否有理论上的限制阻止Java编译器在程序运行之前捕获它,或者它是一个实现选择?
这是一个实现选择,不是为了说明它的特殊情况。