java.lang.ClassException:A 不能被强制转换为 B

2022-09-03 04:51:53

我实现了这个代码:

class A {
    //some code
}
class B extends A {
    // some code
}

class C {
    public static void main(String []args)
    {
        B b1 = (B) new A();
        A a1 = (B) new A();
    }
}

这两行,当单独编译时,编译良好,但使用java.lang.ClassException会给出运行时错误:A不能转换为B

为什么它们编译得很好,但给出了运行时错误?


答案 1

类型的变量可以存储对类型对象或其子类型的引用,就像在 case 类中一样。AAB

因此,可以有如下代码:

A a = new B();

变量是类型,因此它只能访问该类的API,它无法访问在B类中添加的方法,它引用了哪个对象。但有时我们希望能够访问这些方法,因此应该可以以某种方式将引用存储在某个更准确的类型变量中(此处),通过这些变量,我们可以从B类访问这些附加方法。
aAaB

让我们尝试以这种方式实现它:

B b = a;//WRONG!!! "Type mismatch" error

此类代码给出编译时错误。它碰巧将我们从这样的情况中拯救出来:Type mismatch

  • class B1 extends A
  • class B2 extends A

    我们有.A a = new B1();

    现在让我们尝试分配 .请记住,编译器不知道变量a下实际保存的内容,因此它需要生成对所有可能值都是安全的代码。如果编译器不抱怨它也应该允许编译 。因此,为了安全起见,它不让我们这样做。B1 b = a;B1 b = a;B2 b = a;

    那么我们应该如何从中分配引用?我们需要明确地告诉编译器,我们在这里知道潜在的类型不匹配问题,但是我们确信,保存在 中的引用可以安全地分配到 类型的变量中。我们通过 将值从 转换为类型来执行此操作。aB1aBaB(B)a

    B b = (B)a;
    

但是,让我们回到您问题中的示例

B b1 = (B) new A();
A a1 = (B) new A();

new运算符返回与创建对象相同类型的引用,因此返回该类型的引用,因此new A()A

B b1 = (B) new A();

可视作

A tmp = new A();
B b1 = (B) tmp;

这里的问题是,您不能将对超类对象的引用存储在其派生类型的变量中
为什么存在这样的限制?假设派生类添加了一些超类型没有的新方法

class A {
    // some code
}

class B extends A {
    private int i;
    public void setI(int i){
        this.i=i;
    }
}

如果允许这样做

B b = (B)new A();

你以后可能会以调用 结束。但它是正确的吗?否,因为类 A 的实例没有该方法使用的方法或字段b.setI(42);setIi

因此,为了防止在运行时出现这种情况,抛出。(B)new A();java.lang.ClassCastException


答案 2

它在运行时失败的原因是对象不是 B。这是一个A。因此,虽然有些A可以转换为B,但您的不能。

编译器无法分析发生在 A 对象上的所有内容。例如。

A a1 = new B();
A a2 = new A();

B b1 = (B) a1;    // Ok
B b2 = (B) a2;    // Fails

因此,编译器不确定您的 A 对象是否实际上可转换为 B。因此,在上面,它会认为最后2行是可以的。但是当你实际运行程序时,它意识到这不是一个B,它只是一个A。a2


推荐