从子类调用方法会导致运行时ClassCastException

时间:2014-04-29 11:42:12

标签: java classcastexception

当我运行下面的源代码时,我在shell窗口中看到的运行时错误如下:

Exception in thread "main" java.lang.ClassCastException: pkgs.main.A cannot be c
ast to pkgs.test.B
        at pkgs.main.Main.main(Main.java:9)

编译时编译器是否应该捕获此错误,而不是在运行时?这是我的三个单独的源代码文件,它们共同编译好,没有错误或警告:

// ------------------------
// class Main, package main
// ------------------------
package pkgs.main;
import pkgs.test.B;

class Main {
    static public void main(String args[]) {
    ((B)new A()).m();  // causes runtime ClassCastException to be raised.
    }
}


// ---------------------
// class A, package main
// ---------------------
package pkgs.main;

public class A {
    public int m() { return 1; }
}


// ---------------------
// class B, package test  <--- note the different package to classes A and Main
// ---------------------
package pkgs.test;
import pkgs.main.A;

public class B extends A {
    public int m() { return 100; }
}

以下是基于shell的编译和运行批处理文件的内容:

REM batch file for compilation
javac -Xlint -sourcepath ..\src -d ..\cls ..\src\pkgs\main\Main.java

REM batch file for execution
java -cp ..\cls pkgs.main.Main

这个非常简单的测试项目可以通过以下DropBox链接快速下载和测试:https://www.dropbox.com/s/60qi0vbadc5m7es/t16_mk2.7z

4 个答案:

答案 0 :(得分:2)

此处是否引发了编译错误的规则由5.5.1

给出
  

给定编译时引用类型S(源)和编译时引用类型T(目标),如果由于以下规则而没有发生编译时错误,则从S到T存在转换转换。

     

如果S是班级类型:

     
      
  • 如果T是类类型,则为| S | &lt ;: | T |,或| T | &lt;:| S |。否则,将发生编译时错误。
  •   

这意味着如果任何一个类型是另一个类型的子类,它就是一个有效的强制转换。它以这种方式工作,以便我们可以做这样的事情:

A actuallyB = new B(); // note implicit widening but could be (A)new B()
B theB = (B)actuallyB;

人类很容易辨别表达式(B)new A()将在运行时抛出;但是,编译器无需进行此识别。

答案 1 :(得分:1)

BA 子类 。因此,A 的实例不能转换为B。反过来也是可能的。

B b = new B();
A a = (A)b; //valid

A a = new A();
B b = (B)a; //invalid

您可能需要阅读IS A relationship

答案 2 :(得分:1)

  

编译时是否应该由编译器捕获此错误,而不是   在运行时?

它应该在运行时捕获。

<强>为什么吗

在课程Hirearchy class B extends A上,请考虑以下代码:

void perforAction(A obj) {
    obj.someMethodDeclaredInA();
    //more method calls from A
    if (obj instanceof B) {
        B casted = (B) obj;
        casted.someMethodDeclaredInB();
    }
}

这个想法是,这个方法的第一部分处理A和B的对象,第二部分对B. Howerver的对象执行一些额外的动作,你必须施放{{ 1}}到A来实现这一点,所以这不能是编译错误,因为这样的代码就会被编译。

答案 3 :(得分:0)

您正在尝试将A的实际对象(即父级)转换为B的对象引用(即子级)。所以它给出了错误。

它是否是B的实际对象;但引用为A的对象;那么类型铸造就会成功。例如

B obj_of_B = new B();
A obj_of_A = obj_of_B;
B other_obj_of_B = (B)obj_of_A;

这将正常工作,就好像名为“obj_of_A”的对象引用是A类型,实际对象是B类型。

因此要理解“对象引用类型”和“实际对象类型”之间的区别。只有在运行时,您才会知道“实际对象的类型”。所以这个错误将在运行时抛出。