ClassCastException与“无法强制转换”编译错误

为我的OCA Java SE 7程序员考试,所以新手问题。 我有一个我不明白的例子问题。 以下代码编译,但在运行时给出ClassCastException:

interface Roamable { } class Phone { } public class Tablet extends Phone implements Roamable { public static void main(String... args) { Roamable var = (Roamable) new Phone(); } } 

当我改变Roamable var = (Roamable) new Phone(); 进入Roamable var = (Roamable) new String(); 我马上收到编译错误。

两个问题:

  1. 为什么上面的代码完全编译? 电话似乎与Roamable无关?
  2. 为什么代码用new Phone()编译,但不用new String()编译?

为什么上面的代码完全编译? 电话似乎与Roamable无关?

是的,因为Roamable是一个接口,它可能会导致运行时exception而不是编译时exception,因为即使Phone没有实现Roamable ,也可能是Phone的子类,因此编译器无法知道它但是在运行时。

它已经在java语言规范中定义。 在这里查看我的答案

为什么代码用新的Phone()编译,但不用新的String()编译?

因为class Stringjava.lang包中声明为public final class 。 正如jls 8.1.1.2 final class部分所指定的:声明为final的类不能被扩展,因此它不会有任何子类。 因此,编译器已经知道String不能被扩展:因此没有子类的存在可以实现接口 Roamable

编辑:(回复您的以下评论)

让我们假设BA的子类,它实现了接口T

现在声明:

  T t = (T)new A(); 

基本上与:

  A aObj = new A() ; T t = (T)aObj ; // a run-time exception happen 

在得出结论之前,让我们用B的对象做同样的事情:

  A aObj = new B(); T t = (T)aObj; // no exception happen. 

所以,这里有超类和子类的真正原因是参考。 第二个代码示例中的aObj类也是A类的实例,但它也是已实现T B类实例。

字符串是最终的,因此无法将其转换为Roamable。

代码编译是因为编译器允许您转换为任何内容。 这是程序员的明确操作,编译器假定您已经评估了风险并采取了适当的预防措施。

String不是Roamable的实例,因此您无法将String实例分配给Roamable引用。 这可以在编译时确定,因此失败了。

new Phone()侵入了Phone类,它可能是实现RoamablePhone的扩展(就编译器而言)。

如果你使Phone成为final类(如String),你将得到编译器错误。

一个好问题。 new Phone()绝对不是Phone任何子类。 但是,这些信息丢失了,javac在那里看到了Phone类型,而不是确切的 Phone类型。

相关规范: http : //docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

如果S是最终类(第8.1.1节),那么S必须实现T,

规范可以修改

如果表达式是类实例创建表达式(第15.9节),则表达式的类型必须是T的子类型。

首先阅读java关于Explicit和Implicit类型转换的内容。

从那个用户负责显式转换时,缩小对象关系,说用户知道并且因为这样,他们会失去一些精度。 但是仍然编译器可以检测到一些显式的错误转换并抛出CE: Type mismatch error在某些时候输入CE: Type mismatch error 。 除此之外,还需要运行时ClassCastException来处理它。

编译器可以检测以下显式转换的情况。

 class A {} class B {} A a = (A) new B(); //CE: Type mismatch: cannot convert from B to A B b = (B) new A(); //compilation error (CE) 

 interface I {} final class A {} I i = (I) new A(); //compilation error 

编译器无法检测到以下显式转换的情况。

 class A {} class B extends A {} A a = (A) new B(); //Fine B b = (B) new A(); //Runtime error; because compile time; //compiler wont be able to tell the reference is instance of A or B. //means this is something like below. 
B b = (B) (A) new A();

任何对象都可以在没有编译错误的情况下进入任何接口。

 interface I {} class A {} class B extends A implements I{} I i = (I) new A(); //Runtime error I i = (I) new B(); //Fine 

为什么编译?

接口的引用可以在没有编译错误的情况下连接到任何对象。

 interface I {} class A implements I {} class B {} B b = (B) getI(); //Runtime error OR B b = (B)(I)new A(); //Runtime error public I getI() { return new A(); }