Java 为什么这个编译很好(下面是明显的运行时异常)

Java 为什么这个编译很好(下面是明显的运行时异常),java,Java,我有一个超类动物和两个子类:猫和狗。Dog类有一个新的方法,该方法是自己独有的,称为playFetch() 现在,我这样做: Animal G = new Cat("Tom"); ((Dog) G).playFetch(); 那么,编译器在编译它之前到底是怎么想的呢。它是这样想的吗?“好的,特伦特正在引用类型将一个动物对象引用转换成一个狗对象引用,我认为这没有什么错,因为狗也是一个动物,狗有playFetch()方法。” 在运行期间,我们知道会发生什么。请说服我为什么编译器没有抱怨。或者我的论

我有一个超类动物和两个子类:猫和狗。Dog类有一个新的方法,该方法是自己独有的,称为playFetch()

现在,我这样做:

Animal G = new Cat("Tom");
((Dog) G).playFetch();
那么,编译器在编译它之前到底是怎么想的呢。它是这样想的吗?“好的,特伦特正在引用类型将一个动物对象引用转换成一个狗对象引用,我认为这没有什么错,因为狗也是一个动物,狗有playFetch()方法。”


在运行期间,我们知道会发生什么。请说服我为什么编译器没有抱怨。或者我的论点正确吗?

强制转换总是强制转换,正确使用它取决于程序员。也许您正试图为了单元测试而生成ClassCastException(“猫不应该扩展狗”),或者有其他编译器无法理解的计划。因此,当您使用告诉编译器退出的功能时,编译器会退出,而强制转换就是这些功能之一


(这也解释了规范允许编译器这么做的原因。)

编译器认为它应该符合Java语言规范,包括

如果操作数的编译时类型不正确,则为编译时错误 决不能按照强制转换运算符指定的类型进行强制转换 铸造转换规则(§5.5)

否则,在运行时,将转换操作数值(如果需要) 通过强制转换转换为强制转换运算符指定的类型

该规范不需要进行数据流分析,以确定特定强制转换的操作数始终是不兼容的子类型。可编译Java程序的定义应该独立于编译器的选择

如果您想让编译器知道G总是引用猫,请将其声明为猫的类型,而不是动物。

,因为狗是
动物。具体而言,这条规则正在实施:


如果T是一个类类型,那么| S | a cast中的任何一个都是编译器将引用视为对指定类型的对象的引用的请求。i、 e.在这种情况下不抱怨的请求。幸运的是,在Java中,有运行时类型检查,因此您会得到一个相当大的错误。在其他语言中,您实际上不会在运行时得到错误…只是奇怪的结果。您已经对其进行了铸造,因此您需要对其是否在逻辑上有意义负责。虽然这种情况显然是一个错误,但编译器要确定是否进行了不正确的强制转换,即使不是不可能,也不会太困难。也许你应该看看静态代码分析。我不知道。如果这个单元测试真的进入了我的测试套件,我真的需要花很长的时间仔细看看它是如何设计的。这看起来更像是一个糟糕的设计决策。@Makato这只是一个例子(并不是说使用单元测试来防止糟糕的设计决策有问题)