15

为我的 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. 为什么上面的代码完全编译?电话对我来说似乎与可漫游无关?
  2. 为什么代码可以用 编译new Phone(),但不能用 编译new String()
4

6 回答 6

13

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

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

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

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

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

于 2013-11-10T21:39:39.677 回答
3

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

于 2013-11-10T21:24:03.280 回答
1

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

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

于 2013-11-10T21:21:30.087 回答
1

new Phone()evals 进入 class Phone,这可能Phone该实现的扩展Roamable(就编译器而言)。

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

于 2013-11-10T21:21:44.790 回答
0

一个好问题。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 的子类型。

于 2013-11-11T00:23:01.070 回答
0

首先阅读java 的显式和隐式类型转换

在缩小对象关系时,该用户负责显式转换,以表示用户知道并且可以因此而失去一些精度。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

为什么会这样编译?

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

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();
}
于 2016-05-12T15:06:01.680 回答