java 从超类到子类的显式强制转换

nwsw7zdq  于 2023-01-07  发布在  Java
关注(0)|答案(8)|浏览(138)
public class Animal {
    public void eat() {}
}

public class Dog extends Animal {
    public void eat() {}

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = (Dog) animal;
    }
}

赋值语句Dog dog = (Dog) animal;不会产生编译错误,但是在运行时会产生一个ClassCastException,为什么编译器检测不到这个错误?

3lxsmp7m

3lxsmp7m1#

通过使用强制类型转换,你实际上是在告诉编译器,“相信我,我是专业人士,我知道我在做什么,我知道,尽管你不能保证,但我告诉你,这个animal变量肯定会是一只狗。”
由于动物实际上不是狗(它是动物,您可以执行Animal animal = new Dog();,它将是狗),VM在运行时抛出异常,因为您违反了这种信任(您告诉编译器一切都会好的,但它不是!)
编译器比盲目地接受一切要聪明一些,如果你试图在不同的继承层次中转换对象(例如,将一个Dog转换为一个String),那么编译器会将它扔还给你,因为它知道这永远不可能工作。
因为您实际上只是在阻止编译器发出抱怨,所以每次进行强制类型转换时,都必须检查在if语句中使用instanceof(或类似的语句)是否会导致ClassCastException

xpszyzbs

xpszyzbs2#

因为理论上Animal animal可以是狗

Animal animal = new Dog();

一般来说,向下转换不是一个好主意。您应该避免它。如果您使用它,最好包括一个检查:

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}
5gfr0r5j

5gfr0r5j3#

为了避免此类ClassCastException,如果您有:

class A
class B extends A

你可以在B中定义一个构造函数,它接受A的一个对象。这样我们就可以进行“强制转换”,例如:

public B(A a) {
    super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
    // If B class has more attributes, then you would initilize them here
}
iugsix8n

iugsix8n4#

  • 迈克尔·贝里(Michael Berry)给出了详细的答案 *

第一个月
在这里,您对编译器说“相信我。我知道d确实引用了Dog对象”,尽管它不是。请记住,当我们执行向下转换时,编译器被迫信任我们
编译器只知道声明的引用类型,而JVM在运行时知道对象的真实情况。
因此,当JVM在运行时发现Dog d实际上引用的是Animal,而不是Dog对象时,它会说:嘿......你对编译器撒了谎,抛出了一个很大的ClassCastException
因此,如果你是向下预测,你应该使用instanceof测试,以避免搞砸。
if (animal instanceof Dog) { Dog dog = (Dog) animal; }
现在一个问题出现在我们的脑海中,为什么编译器最终会抛出一个java.lang.ClassCastException,却允许向下转换?
答案是,编译器所能做的就是验证这两种类型是否在同一个继承树中,因此根据向下转换之前可能出现的代码,animal可能是dog类型。
编译器必须允许在运行时可能工作的东西。
考虑以下代码片段:

public static void main(String[] args) 
{   
    Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
    Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
    d.eat();

}

private static Animal getMeAnAnimal()
{
    Animal animal = new Dog();
    return animal;
}

然而,如果编译器确信强制转换不可能工作,编译就会失败。也就是说,如果你试图在不同的继承层次结构中强制转换对象
String s = (String)d; // ERROR : cannot cast for Dog to String
与向下转换不同,向上转换是隐式工作的,因为当向上转换时,您隐式地限制了可以调用的方法的数量,这与向下转换相反,向下转换意味着稍后您可能需要调用更具体的方法。
Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast
以上两种向上转换都可以无一例外地工作,因为狗是动物,动物能做的事,狗也能做,但反之则不然。

jexiocij

jexiocij5#

为了推导@Caumons的答案:
假设一个父类有很多子类,并且需要在该类中添加一个公共字段。如果您考虑上述方法,则应该逐个访问每个子类,并为新字段重构它们的构造函数。因此,该解决方案在此场景中不是一个有前途的解决方案
现在来看一下这个解决方案。
父亲可以从每个孩子那里接收一个self对象。下面是一个父亲类:

public class Father {

    protected String fatherField;

    public Father(Father a){
        fatherField = a.fatherField;
    }

    //Second constructor
    public Father(String fatherField){
        this.fatherField = fatherField;
    }

    //.... Other constructors + Getters and Setters for the Fields
}

下面是我们的子类,它应该实现它的父构造函数之一,在本例中是前面提到的构造函数:

public class Child extends Father {

    protected String childField;

    public Child(Father father, String childField ) {
        super(father);
        this.childField = childField;
    }

    //.... Other constructors + Getters and Setters for the Fields

    @Override
    public String toString() {
        return String.format("Father Field is: %s\nChild Field is: %s", fatherField, childField);
    }
}

现在我们测试应用程序:

public class Test {
    public static void main(String[] args) {
        Father fatherObj = new Father("Father String");
        Child child = new Child(fatherObj, "Child String");
        System.out.println(child);
    }
}

结果如下:
父字段为:父字符串
子字段为:子字符串
现在您可以轻松地向父类添加新字段,而不必担心子类代码被破坏;

w8rqjzmb

w8rqjzmb6#

代码生成编译错误,因为您的示例类型是Animal:

Animal animal=new Animal();

Java中不允许向下转换有几个原因。详细信息请参见此处。

kd3sttzy

kd3sttzy7#

如前所述,这是不可能的。如果你想使用子类的一个方法,评估一下把这个方法添加到超类(可能是空的)中的可能性,然后从子类中调用,得到你想要的行为(子类),这要归功于多态性。所以当你调用d.method()时,调用会成功,而不需要强制类型转换,但是如果对象不是一只狗,就不会有问题了

nbysray5

nbysray58#

如前所述,您不能从超类强制转换到子类,除非您的对象首先从子类示例化。
不过,也有一些变通办法。
您所需要的只是一组构造函数和一个方便的方法,该方法可以将对象强制转换为Dog,也可以返回一个具有相同Animal属性的新Dog对象。
下面是一个例子,它可以做到这一点:

public class Animal {

    public Animal() {}

    public Animal(Animal in) {
        // Assign animal properties
    }

    public Dog toDog() {
        if (this instanceof Dog)
            return (Dog) this;
        return new Dog(this);
    }

}

public class Dog extends Animal {

    public Dog(Animal in) {
        super(in);
    }

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = animal.toDog();
    }

}

相关问题