我实现了以下代码:

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 cannot be cast into B

为什么它们编译良好,却给出运行时错误?

最佳答案

类型A的变量可以存储对A类型的对象或其子类型的引用,例如在您的案例类B中。

因此,可能会有类似以下的代码:

A a = new B();

变量a的类型为A,因此它只能访问该类的API,而不能访问在类B中添加的方法(即它引用的对象)。但是有时我们希望能够访问这些方法,因此应该可以将a中的引用存储在更准确类型的某个变量(此处为B)中,通过该变量我们可以访问类B中的其他方法。
但是我们该怎么做?

让我们尝试通过以下方式实现它:
B b = a;//WRONG!!! "Type mismatch" error

这样的代码会导致编译时出现Type mismatch错误。碰巧使我们免于这种情况:
  • class B1 extends A
  • class B2 extends A
    我们有A a = new B1();

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

    那么我们应该怎么做才能将引用从a分配给B1?我们需要在此明确告知编译器我们知道潜在的类型不匹配问题,但是我们确保可以将a中保存的引用安全地分配给B类型的变量。我们通过将值从a转换为通过B键入(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;
    

    这里的问题是您不能在其派生类型的变量中存储对父类(super class)对象的引用。
    为什么存在这种限制?可以说派生类添加了一些父类(super class)型不喜欢的新方法
    class A {
        // some code
    }
    
    class B extends A {
        private int i;
        public void setI(int i){
            this.i=i;
        }
    }
    

    如果允许的话
    B b = (B)new A();
    

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

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

    关于java - java.lang.ClassException : A cannot be cast into B,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17217965/

    10-14 12:12
    查看更多