Downcasting in Java


178

Upcasting ist in Java zulässig, Downcasting führt jedoch zu einem Kompilierungsfehler.

Der Kompilierungsfehler kann durch Hinzufügen einer Umwandlung behoben werden, würde aber zur Laufzeit trotzdem unterbrochen.

In diesem Fall, warum erlaubt Java Downcasting, wenn es zur Laufzeit nicht ausgeführt werden kann?
Gibt es eine praktische Verwendung für dieses Konzept?

public class demo {
  public static void main(String a[]) {
      B b = (B) new A(); // compiles with the cast, 
                         // but runtime exception - java.lang.ClassCastException
  }
}

class A {
  public void draw() {
    System.out.println("1");
  }

  public void draw1() {
    System.out.println("2");
  }
}

class B extends A {
  public void draw() {
    System.out.println("3");
  }
  public void draw2() {
    System.out.println("4");
  }
}

9
Ein Beispiel für einen Codeausschnitt plus den Fehler würde dies zu einer besseren Frage für Leute machen, die versuchen, die Konzepte zu lernen.
Bob Cross

3
+1 für Bobs Kommentar. Die Frage ist überhaupt nicht klar.
Jon Skeet

Ich sehe, dass das obige Beispiel aus speedreviews.com/forums/t151266-downcasting-problem.html stammt, das bereits einige gute Antworten hat.
PhiLho

2
@PhiLho - Joels Hauptabsicht war es, all die tollen Fragen und Antworten unter einem gemeinsamen Dach zu haben. Es spielt keine Rolle, ob die Frage / der Code / die Antworten bereits auf einigen anderen Websites veröffentlicht wurden. Ich hoffe, Sie verstehen es, sonst hören Sie sich Joels Podcasts an.
Allmächtig

Bitte bearbeiten Sie dies so, dass die Codefragmente alle um vier Leerzeichen eingerückt sind. Dadurch wird die Formatierung behoben.
schlank

Antworten:


297

Downcasting ist zulässig, wenn die Möglichkeit besteht, dass es zur Laufzeit erfolgreich ist:

Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String

In einigen Fällen wird dies nicht gelingen:

Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String

Wenn ein Cast (wie dieser letzte) zur Laufzeit fehlschlägt, ClassCastExceptionwird a geworfen.

In anderen Fällen wird es funktionieren:

Object o = "a String";
String s = (String) o; // this will work, since o references a String

Beachten Sie, dass einige Casts beim Kompilieren nicht zulässig sind, da sie niemals erfolgreich sein werden:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.

Object o = new Object(); String s = (String) o;Es funktioniert gut für mich ..: O wie?
Asif Mushtaq

@UnKnown: sollte es nicht. Stellen Sie sicher , dass Sie diese Version tatsächlich kompiliert und ausgeführt haben. Wenn Sie sie noch reproduzieren können, stellen Sie eine separate Frage (mit einer SSCCE ).
Joachim Sauer

@ JoachimSauer was meinst du mit dieser Version? Ich benutze Java 8.
Asif Mushtaq

1
@UnKnown: Ich meine, der Code, den Sie gepostet haben, sollte nicht ausgeführt werden (er wird kompiliert, löst aber zur Laufzeit eine Ausnahme aus). Diese Kommentare sind nicht der Platz, um das zu debuggen. Bitte posten Sie eine separate Frage.
Joachim Sauer

Wie schlägt das Casting zur Laufzeit fehl? Setzt die Zielobjektreferenz auf null? Wirft eine Ausnahme?
CygnusX1

17

Anhand Ihres Beispiels können Sie Folgendes tun:

public void doit(A a) {
    if(a instanceof B) {
        // needs to cast to B to access draw2 which isn't present in A
        // note that this is probably not a good OO-design, but that would
        // be out-of-scope for this discussion :)
        ((B)a).draw2();
    }
    a.draw();
}

Ich habe gerade gelernt, wie wichtig Instanzen sind, als meine abstrakte Klasse um mehrere Klassen erweitert wurde, und ich wollte exklusive Methoden dieser Klassen verwenden, während ich mich auf den abstrakten Klassentyp bezog. Ich habe keine Instanz von verwendet. Ich hatte eine Klassenbesetzungsausnahme
Tarun

16

Ich glaube, dass dies für alle statisch typisierten Sprachen gilt:

String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String

Der Typecast sagt effektiv: Nehmen Sie an, dass dies ein Verweis auf die Besetzungsklasse ist, und verwenden Sie ihn als solchen. Nehmen wir nun an, o ist wirklich eine Ganzzahl, vorausgesetzt, dies ist ein String, macht keinen Sinn und führt zu unerwarteten Ergebnissen. Daher muss eine Laufzeitprüfung und eine Ausnahme durchgeführt werden, um die Laufzeitumgebung darüber zu informieren, dass etwas nicht stimmt.

In der Praxis können Sie Code schreiben, der an einer allgemeineren Klasse arbeitet, ihn jedoch in eine Unterklasse umwandeln, wenn Sie wissen, um welche Unterklasse es sich handelt, und ihn als solche behandeln müssen. Ein typisches Beispiel ist das Überschreiben von Object.equals (). Angenommen, wir haben eine Klasse für Auto:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}

Ich mag das Wort Wirklich und ich werde Ihren Beitrag bearbeiten, um ihn offensichtlicher zu machen
Charaf JRA

5

Wir alle können sehen, dass der von Ihnen bereitgestellte Code zur Laufzeit nicht funktioniert. Das ist , weil wir wissen , dass der Ausdruck new A()kann nie ein Objekt vom Typ sein B.

Aber so sieht es der Compiler nicht. Wenn der Compiler prüft, ob die Umwandlung zulässig ist, sieht er nur Folgendes:

variable_of_type_B = (B)expression_of_type_A;

Und wie andere gezeigt haben, ist diese Art der Besetzung vollkommen legal. Der Ausdruck auf der rechten Seite könnte sehr gut zu einem Objekt vom Typ ausgewertet werden B. Der Compiler sieht das Aund Bhat eine Subtyp-Beziehung, sodass mit der "Ausdruck" -Ansicht des Codes die Umwandlung möglicherweise funktioniert.

Der Compiler berücksichtigt den Sonderfall nicht, wenn er genau weiß , welchen Objekttyp er expression_of_type_Awirklich haben wird. Es sieht nur den statischen Typ als Aund betrachtet den dynamischen Typ als Aoder einen Nachkommen davon A, einschließlich B.


3

In diesem Fall, warum erlaubt Java Downcasting, wenn es zur Laufzeit nicht ausgeführt werden kann?

Ich glaube, das liegt daran, dass der Compiler zur Kompilierungszeit nicht wissen kann, ob die Besetzung erfolgreich sein wird oder nicht. In Ihrem Beispiel ist es einfach zu erkennen, dass die Besetzung fehlschlägt, aber es gibt andere Zeiten, in denen dies nicht so klar ist.

Stellen Sie sich zum Beispiel vor, dass die Typen B, C und D alle den Typ A erweitern, und dann gibt eine Methode public A getSomeA()abhängig von einer zufällig generierten Zahl eine Instanz von B, C oder D zurück. Der Compiler kann nicht wissen, welcher genaue Laufzeittyp von dieser Methode zurückgegeben wird. Wenn Sie also die Ergebnisse später umwandeln, können Sie nicht feststellen B, ob die Umwandlung erfolgreich ist (oder fehlschlägt). Daher muss der Compiler davon ausgehen, dass Casts erfolgreich sind.


2

@ Original Poster - siehe Inline-Kommentare.

public class demo 
{
    public static void main(String a[]) 
    {
        B b = (B) new A(); // compiles with the cast, but runtime exception - java.lang.ClassCastException 
        //- A subclass variable cannot hold a reference to a superclass  variable. so, the above statement will not work.

        //For downcast, what you need is a superclass ref containing a subclass object.
        A superClassRef = new B();//just for the sake of illustration
        B subClassRef = (B)superClassRef; // Valid downcast. 
    }
}

class A 
{
    public void draw() 
    {
        System.out.println("1");
    }

    public void draw1() 
    {
        System.out.println("2");
    }
}

class B extends A 
{
    public void draw() 
    {
        System.out.println("3");
    }

    public void draw2() 
    {
        System.out.println("4");
    }
}

1

Downcast funktioniert in dem Fall, in dem es sich um ein Upcast-Objekt handelt. Upcasting:

int intValue = 10;
Object objValue = (Object) intvalue;

Jetzt kann diese objValueVariable immer herabgestuft werden, intda das gegossene Objekt ein Integer,

int oldIntValue = (Integer) objValue;
// can be done 

aber weil objValuees ein Objekt ist, kann es nicht gewirkt werden, Stringweil es nicht gewirkt werden intkann String.


0

Downcasting ist sehr nützlich in dem folgenden Code-Snippet, das ich ständig benutze. Dies beweist, dass Downcasting nützlich ist.

private static String printAll(LinkedList c)
{
    Object arr[]=c.toArray();
    String list_string="";
    for(int i=0;i<c.size();i++)
    {
        String mn=(String)arr[i];
        list_string+=(mn);
    }
    return list_string;
}

Ich speichere String in der verknüpften Liste. Wenn ich die Elemente der verknüpften Liste abrufe, werden Objekte zurückgegeben. Um auf die Elemente als Strings (oder andere Klassenobjekte) zuzugreifen, hilft mir das Downcasting.

Mit Java können wir Downcast-Code kompilieren und uns darauf verlassen, dass wir das Falsche tun. Wenn Menschen einen Fehler machen, wird er zur Laufzeit abgefangen.


Die Verwendung nicht generischer Sammlungen in Java entspricht void*Zeigern in C ++. Es klingt für mich überhaupt nicht nach einer guten Idee.
Jezor

0

Betrachten Sie das folgende Beispiel

public class ClastingDemo {

/**
 * @param args
 */
public static void main(String[] args) {
    AOne obj = new Bone();
    ((Bone) obj).method2();
}
}

class AOne {
public void method1() {
    System.out.println("this is superclass");
}
}


 class Bone extends AOne {

public void method2() {
    System.out.println("this is subclass");
}
}

Hier erstellen wir das Objekt der Unterklasse Bone und weisen es der Superklasse AOne-Referenz zu. Jetzt kennt die Superklasse-Referenz die Methode method2 in der Unterklasse, dh Bone, während der Kompilierungszeit nicht. Daher müssen wir diese Referenz der Superklasse auf die Unterklassenreferenz herabstufen, um Die resultierende Referenz kann über das Vorhandensein von Methoden in der Unterklasse, dh Knochen, informiert werden


AOne sieht etwas verwirrend aus. Bitte erwägen Sie, Ihre Klassennamen in Hund und Tier oder so zu ändern
Kartik Chugh

0

Um Downcasting in Java durchzuführen und Laufzeitausnahmen zu vermeiden, verweisen Sie auf den folgenden Code:

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

Hier ist Tier die Elternklasse und Hund die Kinderklasse.
instanceof ist ein Schlüsselwort, mit dem überprüft wird, ob eine Referenzvariable einen bestimmten Typ von Objektreferenz enthält oder nicht.


0

Eine Downcasting-Transformation von Objekten ist nicht möglich. Nur

DownCasting1 _downCasting1 = (DownCasting1)((DownCasting2)downCasting1);

ist möglich

class DownCasting0 {
    public int qwe() {
        System.out.println("DownCasting0");
        return -0;
    }
}

class DownCasting1 extends DownCasting0 {
    public int qwe1() {
        System.out.println("DownCasting1");
        return -1;
    }
}

class DownCasting2 extends DownCasting1 {
    public int qwe2() {
        System.out.println("DownCasting2");
        return -2;
    }
}

public class DownCasting {

    public static void main(String[] args) {

        try {
            DownCasting0 downCasting0 = new DownCasting0();
            DownCasting1 downCasting1 = new DownCasting1();
            DownCasting2 downCasting2 = new DownCasting2();

            DownCasting0 a1 = (DownCasting0) downCasting2;
            a1.qwe(); //good

            System.out.println(downCasting0 instanceof  DownCasting2);  //false
            System.out.println(downCasting1 instanceof  DownCasting2);  //false
            System.out.println(downCasting0 instanceof  DownCasting1);  //false

            DownCasting2 _downCasting1= (DownCasting2)downCasting1;     //good
            DownCasting1 __downCasting1 = (DownCasting1)_downCasting1;  //good
            DownCasting2 a3 = (DownCasting2) downCasting0; // java.lang.ClassCastException

            if(downCasting0 instanceof  DownCasting2){ //false
                DownCasting2 a2 = (DownCasting2) downCasting0;
                a2.qwe(); //error
            }

            byte b1 = 127;
            short b2 =32_767;
            int b3 = 2_147_483_647;
//          long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
            long b4 = 9_223_372_036_854_775_807L;
//          float _b5 = 3.4e+038; //double default
            float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
            double b6 = 1.7e+038;
            double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits

            long c1 = b3;
            int c2 = (int)b4;

            //int       4 bytes     Stores whole numbers from -2_147_483_648 to 2_147_483_647
            //float     4 bytes     Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
            float c3 = b3; //logic error
            double c4 = b4; //logic error


        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.