Explizites Casting von der Oberklasse zur Unterklasse

Lesezeit: 6 Minuten

Benutzeravatar von saravanan
saravanan

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 generiert es einen ClassCastException. Warum kann der Compiler diesen Fehler nicht erkennen?

  • SIE teilen dem Compiler mit, den Fehler NICHT zu erkennen.

    – Moritz

    1. Februar 2011 um 13:14 Uhr

Benutzeravatar von Michael Berry
Michael Berry

Indem Sie einen Cast verwenden, sagen Sie dem Compiler im Wesentlichen: „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 eigentlich kein Hund ist (es ist ein Tier, könnten Sie tun Animal animal = new Dog(); und es wäre ein Hund) löst die VM zur Laufzeit eine Ausnahme aus, weil Sie dieses Vertrauen verletzt haben (Sie haben dem Compiler gesagt, dass alles in Ordnung wäre, und das ist es nicht!)

Der Compiler ist ein bisschen klüger, als einfach alles blind zu akzeptieren. Wenn Sie versuchen, Objekte in verschiedene Vererbungshierarchien umzuwandeln (zum Beispiel einen Hund in eine Zeichenfolge umzuwandeln), wird der Compiler es Ihnen zurückwerfen, weil er weiß, dass dies möglicherweise niemals funktionieren könnte.

Da Sie den Compiler im Wesentlichen nur daran hindern, sich zu beschweren, ist es wichtig, jedes Mal, wenn Sie umwandeln, zu überprüfen, ob Sie keinen verursachen ClassCastException durch die Nutzung instanceof in einer if-Anweisung (oder so ähnlich)

  • Danke, aber du brauchst einen Hund, der von Animal verlängert wird, wenn nicht, keine Arbeit 🙂

    Benutzer3402040

    9. Dezember 2015 um 10:53 Uhr

  • @delive Natürlich tust du das, aber gemäß der Frage, Dog tut erstrecken sich von Animal!

    – Michael Berry

    25. Mai 2017 um 10:41 Uhr

Denn theoretisch Animal animal dürfen Hund sein:

Animal animal = new Dog();

Im Allgemeinen ist ein Downcasting keine gute Idee. Sie sollten es vermeiden. Wenn Sie es verwenden, fügen Sie besser ein Häkchen hinzu:

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

  • aber der folgende Code erzeugt einen Kompilierungsfehler Dog dog=new Animal(); (inkompatible Typen) .aber in dieser Situation identifiziert der Compiler Animal als Superklasse und Dog als Unterklasse. es akzeptiert. Bitte erklären Sie mir das

    – saravanan

    1. Februar 2011 um 13:19 Uhr


  • ja, denn Animal ist Superklasse. Nicht jedes Tier ist ein Hund, oder? Sie können auf Klassen nur anhand ihrer Typen oder ihrer Supertypen verweisen. Nicht ihre Subtypen.

    – Bozo

    1. Februar 2011 um 13:31 Uhr

Um diese Art von ClassCastException zu vermeiden, wenn Sie Folgendes haben:

class A
class B extends A

Sie können einen Konstruktor in B definieren, der ein Objekt von A übernimmt. Auf diese Weise können wir die “Umwandlung” durchführen, z. B.:

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
}

Benutzeravatar von Zeeshan
Zeeshan

Ausarbeitung der Antwort von Michael Berry.

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

Hier sagen Sie zum Compiler: „Vertrauen Sie mir. Ich weiß d bezieht sich wirklich auf a Dog Objekt”, obwohl dies nicht der Fall ist.
Denken Sie daran, dass der Compiler gezwungen ist, uns zu vertrauen, wenn wir einen Downcast machen.

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

Wenn also die JVM zur Laufzeit herausfindet, dass die Dog d bezieht sich eigentlich auf ein Animal und nicht ein Dog Objekt heißt es. Hey … Sie haben den Compiler angelogen und werfen ein dickes Fett ClassCastException.

Wenn Sie also nach unten schauen, sollten Sie verwenden instanceof testen, um Fehler zu vermeiden.

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

Jetzt kommt uns eine Frage in den Sinn. Warum zum Teufel lässt der Compiler den Downcast zu, wenn er schließlich einen werfen wird java.lang.ClassCastException?

Die Antwort ist, dass der Compiler nur überprüfen kann, ob sich die beiden Typen im selben Vererbungsbaum befinden. Je nachdem, welcher Code vor dem Downcast gekommen ist, ist dies also möglich animal ist vom Typ dog.

Der Compiler muss Dinge zulassen, die möglicherweise zur Laufzeit funktionieren.

Betrachten Sie das folgende 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 Umwandlung nicht funktionieren würde, schlägt die Kompilierung fehl. IE Wenn Sie versuchen, Objekte in verschiedene Vererbungshierarchien umzuwandeln

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

Im Gegensatz zum Downcasting funktioniert das Upcasting implizit, da Sie beim Upcasting im Gegensatz zum Downcasting implizit die Anzahl der Methoden einschränken, die Sie aufrufen können, was bedeutet, 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

Beide oben genannten Upcasts werden ohne Ausnahme gut funktionieren, denn ein Hund IST EIN Tier, was ein Tier tun kann, kann ein Hund tun. Aber umgekehrt stimmt es nicht.

Um die Antwort von @Caumons zu entwickeln:

Stellen Sie sich vor, eine Vaterklasse hat viele Kinder und es besteht die Notwendigkeit, dieser Klasse ein gemeinsames Feld hinzuzufügen. Wenn Sie den erwähnten Ansatz in Betracht ziehen, sollten Sie nacheinander zu jeder untergeordneten Klasse gehen und ihre Konstruktoren für das neue Feld umgestalten. daher ist diese Lösung in diesem Szenario keine vielversprechende Lösung

Schauen Sie sich jetzt diese Lösung an.

Ein Vater kann von jedem Kind 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 untergeordnete Klasse, die einen ihrer Vaterkonstruktoren implementieren sollte, in diesem Fall den oben genannten 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:

Vaterfeld ist: Vaterstring

Untergeordnetes Feld ist: Untergeordnete Zeichenkette

Jetzt können Sie der Vaterklasse ganz einfach neue Felder hinzufügen, ohne sich Sorgen machen zu müssen, dass die Codes Ihrer Kinder gebrochen werden.

Benutzeravatar der Community
Gemeinschaft

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

Animal animal=new Animal();

Downcasting ist in Java aus mehreren Gründen nicht erlaubt. Siehe hier für Details.

Benutzeravatar von fresko
fresko

Wie gesagt, ist nicht möglich. Wenn Sie eine Methode der Unterklasse verwenden möchten, evaluieren Sie die Möglichkeit, die Methode zur Oberklasse hinzuzufügen (möglicherweise leer) und von den Unterklassen aufzurufen, um dank Polymorphismus das gewünschte Verhalten (Unterklasse) zu erhalten. Wenn Sie also d.method() aufrufen, wird der Aufruf ohne Casting erfolgreich sein, aber falls das Objekt kein Hund sein wird, gibt es kein Problem

1445030cookie-checkExplizites Casting von der Oberklasse zur Unterklasse

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy