为什么会出现ClassCastException而不是编译错误?

5

为什么下面的代码我没有得到编译错误?我收到了一个ClassCastException,这有点令人困惑。这是因为它们相关吗?

class Ink {}

Interface Printable {}
class ColorInk extends Ink implements Printable {}

class BlackInk extends Ink {}


class TwistInTaleCasting {
   public static void main(String args[]) {
       Printable printable = null;
       BlackInk blackInk = new BlackInk();
       printable = (Printable)blackInk;
   }
}

1
编译器不会检查显式转换,但运行时会检查。因此,在运行时您会得到ClassCastException异常。 - Pphoenix
2
这是非常基础的内容,但我认为它不应该被投反对票——问题清晰明了,提供了足够的信息来描述问题,发布者需要一个答案。 - heikkim
@Pphoenix:编译器确实会检查显式转换-它会检查转换是否合理。(请参见我的答案中的示例。) - Jon Skeet
@JonSkeet 嗯,既然 BlackInkInk 都不是 可打印的(Printable) ,那么编译器为什么会通过这个转换呢?(或者更确切地说,为什么需要这个转换呢?) - Pphoenix
1
@Pphoenix:再次阅读我的答案——blackInk的值可能是对实现了Printable接口并继承了BlackInk类的类实例的引用。它编译通过的原因与Object o = new Object(); String x = (String) o;编译通过的原因相同… - Jon Skeet
3个回答

11

为什么在下面的代码中我没有收到编译错误?

因为编译器只关心你尝试转换的表达式的静态类型。

看看这两行:

BlackInk blackInk = new BlackInk();
printable = (Printable)blackInk;

知道在第二行中,blackInk的值只由第一行决定其类型为BlackInk对象,但编译器并不知道。对于编译器而言(在编译第二行时),它可能实际上是:

BlackInk blackInk = new PrintableBlackInk();
printable = (Printable)blackInk;

在这里,PrintableBlackInk 是一个扩展了 BlackInk 并实现了 Printable 接口的类。因此,在编译时将类型为 BlackInk 的表达式强制转换为 Printable 是有效的。 如果你将 BlackInk 设为 final 类,则编译器知道它不可能起作用(除非值为 null),并会在编译时失败,如下所示:

error: inconvertible types
          printable = (Printable)blackInk;
                                 ^
required: Printable
found:    BlackInk

具体细节请参见JLS 5.5.1

否则,由于强制类型转换在编译时是有效的,所以我们必须等到执行时才能看到失败。


3
ClassCastException是运行时异常,这就是为什么你可以编译代码但在运行时出现ClassCastException的原因。您也可以查看javadoc - ClassCastException扩展了RuntimeException:http://docs.oracle.com/javase/7/docs/api/java/lang/ClassCastException.html 显然,你之所以会得到ClassCastException,是因为你试图将BlackInk的实例转换为Printable(当你将一个对象强制转换为它不是实例的子类时,你将在运行时得到ClassCastException)。简而言之,在进行类型转换时,需要进行有效操作,否则您将在运行时获得ClassCastException。

1
设置将会正确编译。但在运行时,由于TwistInTaleCastingPrintable没有关系,会抛出类转换异常。由于ClassCastException是**未检查的异常**,因此在代码中不需要处理任何这样的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接