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

Posted

技术标签:

【中文标题】ClassCastException 与“无法转换”编译错误【英文标题】:ClassCastException vs. "cannot cast" compilation error 【发布时间】: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(); 时,我立即收到编译错误。

两个问题:

    为什么上面的代码完全可以编译?电话对我来说似乎与可漫游无关? 为什么代码用new Phone()编译,用new String()编译不行?

【问题讨论】:

编译器知道String是final,因此String的子类不可能是可漫游的。 这是一个很好的问题,让我很长时间没有头绪:) 【参考方案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();

【讨论】:

以上是关于ClassCastException 与“无法转换”编译错误的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.ClassCastException:java.lang.String 无法转换为

ClassCastException:整数无法转换为 Long,同时尝试迭代实体 ID

iText 抛出 ClassCastException:PdfNumber 无法转换为 PdfLiteral

ClassCastException:java.lang.Object[] 无法转换为 java.lang.String[] android

ClassCastException NimbusJwtDecoder 无法转换为 NimbusJwtDecoderJwkSupport

java.lang.ClassCastException:javax.xml.bind.JAXBElement 无法转换为