【问题标题】:Why does this compile just fine (obvious run-time exception follows)为什么这个编译得很好(明显的运行时异常)
【发布时间】:2015-03-02 03:24:59
【问题描述】:

我有一个超类 Animal 和两个子类:Cat 和 Dog。 Dog 类有一个自己独有的新方法,称为 playFetch()。

现在,我这样做:

Animal G = new Cat("Tom");
((Dog) G).playFetch();

那么,编译器在没有任何错误的情况下编译它之前到底是怎么想的。它是否认为...“好吧,Trent 正在将动物对象引用转换为 Dog 对象引用的引用类型,我认为这没有错,因为 Dog 也是动物并且 Dog 具有方法 playFetch()”?

在运行期间,嗯……我们知道会发生什么。请说服我为什么编译器不抱怨。还是我的论点正确?

【问题讨论】:

  • 强制转换是编译器请求将引用视为对指定类型对象的引用。即在这种情况下不抱怨的请求。幸运的是,在 Java 中,有运行时类型检查,所以你会得到一个漂亮的错误。在其他语言中,您实际上不会在运行时收到错误...只是奇怪的结果。
  • 你已经投了它,所以你要对它是否在逻辑上有意义负责。尽管确实这种情况显然是一个错误,但编译器确定是否进行了不正确的转换并不需要太多时间——如果不是不可能的话。或许你应该看看静态代码分析。

标签: java


【解决方案1】:

强制转换总是强制转换,而正确使用它取决于程序员。也许您正在尝试生成 ClassCastException 以进行单元测试(“Cats 永远不应扩展 Dogs”),或者有其他编译器无法理解的计划。因此,当您使用告诉编译器后退的功能时,编译器会后退,而强制转换是这些功能之一。

(这也解释了为什么规范允许编译器这样做。)

【讨论】:

  • 我不知道。如果该单元测试进入我的测试套件,我真的必须仔细研究它是如何设计的。这似乎是一个非常糟糕的设计决策。
  • @Makato 这只是一个例子(并不是说使用单元测试来防止错误的设计决策存在问题)
【解决方案2】:

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

如果操作数的编译时类型可能是编译时错误 根据 转换规则 (§5.5)。

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

该规范不要求进行确定特定强制转换的操作数始终是不兼容子类型所需的那种数据流分析。可编译的 Java 程序的定义应该与编译器的选择无关。

如果您希望编译器知道 G 始终引用 Cat,请将其声明为 Cat 类型,而不是 Animal。

【讨论】:

    【解决方案3】:

    The cast is legal by the JLS,因为DogAnimal。具体来说,这条规则正在被强制执行:

    如果 T 是类类型,那么 |S| <:>

    请注意,它确实说编译时间。生成的 ClassCastException 发生在运行时,特别是当您要转换的目标类无效时。

    通常,您可以将继承链向上转换(即DogAnimalDogObject),但不能转换向下(没有什么说AnimalDog,或者ObjectDog)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      相关资源
      最近更新 更多