【问题标题】:ClassCastException vs. "cannot cast" compilation errorClassCastException 与“无法转换”编译错误
【发布时间】:2013-11-22 14:27:38
【问题描述】:

为我的 OCA Java SE 7 程序员 I 考试而学习,所以新手问题。我有一个我不明白的示例问题。以下代码可以编译,但在运行时会产生 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. 为什么上面的代码完全可以编译?电话对我来说似乎与可漫游无关?
  2. 为什么代码用new Phone()编译,用new String()编译不行?

【问题讨论】:

  • 编译器知道String是final,因此String的子类不可能是可漫游的。
  • 这是一个很好的问题,让我很长时间没有头绪:)

标签: java inheritance casting classcastexception


【解决方案1】:

为什么上面的代码完全可以编译?电话似乎无关 对我来说可以漫游吗?

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

它已经在java语言规范中定义。 Check out my answer here.

为什么代码用 new Phone() 编译,但没有编译 用新的字符串()?

因为class Stringjava.lang 包中被声明为public final class。如jls 8.1.1.2 final class 部分所述:声明为final 的类不能扩展,因此它不会有任何子类。所以,编译器已经知道 String 不能被扩展:因此没有子类的存在是可能实现 interface 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 类的一个实例,但它也是实现TB 类的一个实例。

【讨论】:

  • 但我示例中的代码表示要创建一个新的Phone,而不是可能扩展Phone 并实现Roamable 的其他类的对象。我不明白为什么编译器在这里关心可能存在的子类?
  • @luukburger,更新了答案。检查提供的示例。假设 A 类为您的 Phone
  • 嗯...感谢您的帮助。所以编译器说:new Phone() 将交给我一个Phone 类型的对象。也许真的是一个Phone,也许是一个实现可漫游的Phone2。它没有看到不存在 Phone2 类,因此该假设是错误的......?
  • Phone2 的存在将在运行时被知道。 :) 不要忘记查看本网站的about page。欢迎来到堆栈溢出
【解决方案2】:

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

【讨论】:

  • 所以就编译器而言,我可以将任何非最终类的对象转换为可漫游对象?
  • @luukburger 是的,因为编译器不知道转换类型的子类是否实现了 Roamable。如果类是最终类,则不能存在这样的子类。
  • 另外,如果你尝试转换为类(假设 Roamable 是类)将导致“无法转换”编译错误
  • 由于java支持类的单继承,可以在编译时识别,但是接口可以在任何子类级别实现(多继承)编译器无法识别。
【解决方案3】:

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

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

【讨论】:

  • 但是Phone 也不是Roamable 的实例?
  • 不是你所拥有的。为此,您需要class Phone implements Roamable。然后Tablet 可以扩展Phone
  • 谢谢,但我的意思是:String 和 Phone (在上面的代码中)都不是 Roamable 的实例,但 new String() 仍然给出编译错误,new Phone() 没有。 ..
  • @luukburger,看看我的回答。我会写在这里,但它会占用空间
【解决方案4】:

new Phone() eval 为 Phone 类,可能是实现 RoamablePhone 的扩展(就编译器而言)。

如果您将 Phone 设为 final 类(如 String),您将收到编译器错误。

【讨论】:

  • 特别是,Tablet 是实现Roamable.Phone 的现有子类,如果Phone 是final,那么让Tablet 类编译将是一个技巧全部:)
  • 抱歉,没有注意到平板电脑正在扩展手机......没什么意义,因为它从未实例化,只是用于主入口点
【解决方案5】:

一个好问题。 new Phone() 绝对不是 Phone 的任何子类。然而,该信息丢失了,javac 在那里看到 Phone 类型,而不是 exact 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 的子类型。

【讨论】:

    【解决方案6】:

    首先阅读 java 的 Explicit and Implicit type casting

    在缩小对象关系范围时,该用户负责显式转换,以表示用户知道并且可以因此而失去一些精度。然而,编译器仍然可以检测到一些明确的错误转换并在某些时候抛出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. <BR>
    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
    

    Why does this compile?

    接口的引用可以转换为任何对象而不会出现编译错误。

    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();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 1970-01-01
      • 2016-11-09
      • 2011-07-15
      • 2018-09-25
      • 1970-01-01
      • 2012-03-16
      相关资源
      最近更新 更多