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
没有实现Roamable
,Phone
的子类也可能,因此编译器没有办法知道它,但在运行时。
它已经在java语言规范中定义。 Check out my answer here.
为什么代码用 new Phone() 编译,但没有编译 用新的字符串()?
因为class String
在java.lang
包中被声明为public final class
。如jls 8.1.1.2 final class
部分所述:声明为final
的类不能扩展,因此它不会有任何子类。所以,编译器已经知道 String
不能被扩展:因此没有子类的存在是可能实现 interface Roamable
编辑:(回复您的以下评论)
让我们假设B
是A
的子类,它实现了接口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
类的一个实例。
【讨论】:
但我示例中的代码表示要创建一个新的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
类,可能是实现 Roamable
的 Phone
的扩展(就编译器而言)。
如果您将 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 无法转换为