当我运行下面的源代码时,我在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
答案 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)
B
是A
的 子类 。因此,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类型。
因此要理解“对象引用类型”和“实际对象类型”之间的区别。只有在运行时,您才会知道“实际对象的类型”。所以这个错误将在运行时抛出。