Frage

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;
    }
}

Die Zuordnung Dog dog = (Dog) animal; generiert keinen Kompilierungsfehler, aber zur Laufzeit erzeugt er a ClassCastException. Warum kann der Compiler diesen Fehler nicht erkennen?

War es hilfreich?

Lösung

Indem Sie eine Besetzung verwenden, die Sie dem Compiler im Wesentlichen sagen: "Vertrauen Sie mir. Ich bin ein Profi, ich weiß, was ich tue, und ich weiß, dass ich Ihnen das sage, obwohl Sie es nicht garantieren können animal Variable wird definitiv ein Hund sein. "

Da das Tier kein Hund ist (es ist ein Tier, könnte man es tun Animal animal = new Dog(); Und es wäre ein Hund) Die VM macht zur Laufzeit eine Ausnahme, weil Sie gegen dieses Vertrauen verstoßen haben (Sie haben dem Compiler gesagt, alles wäre in Ordnung und es ist nicht so!)

Der Compiler ist etwas schlauer, als nur blind alles zu akzeptieren. Wenn Sie versuchen, Objekte in verschiedenen Erbhierarchien zu werfen (zum Beispiel einen Hund in eine Schnur werfen), wird der Compiler ihn auf Sie zurückwerfen, weil er weiß, dass dies möglicherweise nie funktionieren könnte.

Da Sie im Wesentlichen nur den Compiler davon abhalten, sich zu beschweren, ist es wichtig zu überprüfen ClassCastException durch die Nutzung instanceof in einer if -Aussage (oder etwas in diesem Sinne.)

Andere Tipps

Weil theoretisch Animal animal kann ein Hund sein:

Animal animal = new Dog();

Im Allgemeinen ist Downcasting keine gute Idee. Sie sollten es vermeiden. Wenn Sie es verwenden, geben Sie besser einen Scheck an:

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

Um diese Art von ClassCastException zu vermeiden, wenn Sie:

class A
class B extends A

Sie können einen Konstruktor in B definieren, der ein Objekt von A nimmt. Auf diese Weise können wir das "Cast" zB durchführen:

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
}

Ausarbeitung der Antwort von Michael Berry.

Dog d = (Dog)Animal; //Compiles but fails at runtime

Hier sagst du dem Compiler: "Vertrau mir. Ich weiß es d bezieht sich wirklich auf a Dog Objekt "obwohl es nicht ist.Denken Sie daran, der Compiler muss uns vertrauen, wenn wir einen downcast machen.

Der Compiler kennt nur den deklarierten Referenztyp. Die JVM in der Laufzeit weiß, was das Objekt wirklich ist.

Also, wenn der JVM bei der Laufzeit herausfindet, dass die Dog d bezieht sich tatsächlich auf eine Animal und nicht a Dog Objekt, das es heißt. Hey ... du hast den Compiler angelogen und wirft ein großes Fett ClassCastException.

Wenn Sie also downcasting sind, sollten Sie verwenden instanceof Testen Sie, um zu vermeiden, wenn Sie es vermeiden.

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

Jetzt kommt uns eine Frage. Warum zum Hölle Compiler den Niederschlag zulässt, wenn es irgendwann ein werfen wird java.lang.ClassCastException?

Die Antwort lautet, dass der Compiler nur überprüfen kann, ob sich die beiden Typen im selben Erbbaum befinden. Je nachdem, was auch immer der Code vor dem Downcast vorliegt, ist dies möglich, dass dies möglich ist animal ist vom Typ dog.

Der Compiler muss Dinge zulassen, die möglicherweise zur Laufzeit arbeiten können.

Betrachten Sie den folgenden Code -Snipet:

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;
}

Wenn der Compiler jedoch sicher ist, dass die Besetzung nicht möglich ist, wird die Zusammenstellung fehlschlagen. Dh wenn Sie versuchen, Objekte in verschiedenen Vererbungshierarchien zu wirken

String s = (String)d; // ERROR : cannot cast for Dog to String

Im Gegensatz zum Downcasting funktioniert das Upcasting implizit, da Sie implizit die Anzahl der Methoden, die Sie aufrufen können, implizit einschränken können, was dem Downcasting entgegengesetzt ist, was impliziert, dass Sie später möglicherweise eine spezifischere Methode aufrufen möchten.

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

Die beiden oben genannten Aufpassungen funktionieren ohne Ausnahme einwandfrei, da ein Hund ein Tier ist, das ein Tier tun kann, kann ein Hund dies tun. Aber es ist nicht wahr Vica-Versa.

Der Code generiert einen Kompilierungsfehler, weil Ihre Instanztyp ist ein Tier:

Animal animal=new Animal();

Aus mehreren Gründen ist in Java nicht zugelassen. Sehen hier für Details.

Wie erklärt, ist es nicht möglich. Wenn Sie eine Methode der Unterklasse verwenden möchten, bewerten Sie die Möglichkeit, die Methode zur Superklasse hinzuzufügen (möglicherweise leer) und rufen Sie die Unterklassen an, die das gewünschte Verhalten erhalten (Unterklasse) dank des Polymorphismus. Wenn Sie also D.Method () anrufen

Um die Antwort von @caumons zu entwickeln:

Stellen Sie sich vor, eine Vaterklasse hat viele Kinder und es besteht die Notwendigkeit, in dieser Klasse ein gemeinsames Feld hinzuzufügen. Wenn Sie den genannten Ansatz berücksichtigen, sollten Sie zu den einzelnen Kindern der Klasse eins nacheinander gehen und ihre Konstrukteure für das neue Feld refaktorieren. Daher ist diese Lösung in diesem Szenario keine vielversprechende Lösung

Schauen Sie sich nun diese Lösung an.

Ein Vater kann von jedem Kinder ein Selbstobjekt erhalten. Hier ist eine Vaterklasse:

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
}

Hier ist unsere Kinderklasse, die einen seiner Vaterkonstruktor umsetzen sollte, in diesem Fall der oben genannte Konstruktor:

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);
    }
}

Jetzt testen wir die Anwendung:

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);
    }
}

Und hier ist das Ergebnis:

Vater Feld ist: Vater String

Kinderfeld ist: Kinderzeichenfolge

Jetzt können Sie die Vaterklasse leicht neue Felder hinzufügen, ohne sich Sorgen um Ihre Kindercodes zu machen, um zu brechen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top