Downcasting w Javie


179

Upcasting jest dozwolony w Javie, jednak downcasting powoduje błąd kompilacji.

Błąd kompilacji można usunąć, dodając rzutowanie, ale i tak zepsuje się w czasie wykonywania.

W takim przypadku, dlaczego Java pozwala na downcasting, jeśli nie można go wykonać w czasie wykonywania?
Czy istnieje praktyczne zastosowanie tej koncepcji?

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
Przykładowy fragment kodu plus błąd sprawiłby, że byłoby to lepsze pytanie dla osób, które próbują nauczyć się pojęć.
Bob Cross,

3
+1 za komentarz Boba. Pytanie wcale nie jest jasne.
Jon Skeet,

Widzę powyższy przykład pochodzi z velocityreviews.com/forums/t151266-downcasting-problem.html, który ma już kilka dobrych odpowiedzi.
PhiLho,

2
@PhiLho - głównym celem Joela było zebranie wszystkich wielkich pytań i odpowiedzi pod jednym wspólnym parasolem. Nie ma znaczenia, czy pytanie / kod / odpowiedzi zostały już opublikowane w niektórych innych witrynach. Mam nadzieję, że rozumiesz, w przeciwnym razie posłuchaj podcastów Joela.
Wszechmocny

Edytuj to, aby wszystkie fragmenty kodu były wcięte czterema spacjami. To naprawi formatowanie.
szczupły,

Odpowiedzi:


298

Downcasting jest dozwolony, jeśli istnieje możliwość, że się powiedzie w czasie wykonywania:

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

W niektórych przypadkach to się nie powiedzie:

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

Gdy rzutowanie (takie jak ten ostatni) zawiedzie w czasie wykonywania, ClassCastExceptionzostanie wyrzucony.

W innych przypadkach będzie działać:

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

Pamiętaj, że niektóre rzutowania będą niedozwolone w czasie kompilacji, ponieważ nigdy się nie powiedzie:

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;Działa dla mnie dobrze ..: O Jak?
Asif Mushtaq,

@UnKnown: nie powinno. Sprawdź dokładnie , czy rzeczywiście skompilowałeś i uruchomiłeś tę wersję, a jeśli nadal możesz ją odtworzyć, opublikuj osobne pytanie (za pomocą SSCCE ).
Joachim Sauer,

@ JoachimSauer, co masz na myśli przez tę wersję? Używam Java 8.
Asif Mushtaq,

1
@UnKnown: Mam na myśli, że opublikowany kod nie powinien zostać uruchomiony (skompiluje się, ale wygeneruje wyjątek w czasie wykonywania). Te komentarze nie są miejscem do debugowania tego. Proszę zamieścić osobne pytanie.
Joachim Sauer,

Jak rzutowanie kończy się niepowodzeniem w czasie wykonywania? Ustawia odwołanie do obiektu docelowego na null? Zgłasza wyjątek?
CygnusX1

17

Korzystając z Twojego przykładu, możesz:

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

Właśnie dowiedziałem się, jak ważne jest wystąpienie instancji, kiedy moja klasa abstrakcyjna była rozszerzana o wiele klas i chciałem stosować ekskluzywne metody tych klas, odnosząc się do typu klasy abstrakcyjnej. Nie używając instancji miałem wyjątek obsady klas
Tarun,

16

Uważam, że dotyczy to wszystkich języków o typie statycznym:

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

Typecast skutecznie mówi: załóżmy, że jest to odwołanie do klasy obsady i używaj go jako takiego. Załóżmy teraz, że o jest naprawdę liczbą całkowitą, przy założeniu, że jest to ciąg znaków, który nie ma sensu i da nieoczekiwane wyniki, dlatego konieczne jest sprawdzenie środowiska wykonawczego i wyjątek, aby powiadomić środowisko wykonawcze, że coś jest nie tak.

W praktyce możesz napisać kod działający na bardziej ogólnej klasie, ale rzuć go do podklasy, jeśli wiesz, co to jest za podklasę i musisz traktować ją jako taką. Typowym przykładem jest przesłanianie Object.equals (). Załóżmy, że mamy klasę dla samochodów:

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

Podoba mi się słowo Naprawdę i zmodyfikuję twój post, aby był bardziej widoczny
Charaf JRA

5

Wszyscy widzimy, że podany kod nie będzie działał w czasie wykonywania. To dlatego, że wiemy, że wyrażenie nigdy nienew A() może być obiektem typu .B

Ale nie tak to widzi kompilator. Kiedy kompilator sprawdza, czy rzutowanie jest dozwolone, po prostu widzi to:

variable_of_type_B = (B)expression_of_type_A;

I jak wykazali inni, ten rodzaj obsady jest całkowicie legalny. Wyrażenie po prawej stronie może bardzo dobrze ocenić obiekt typu B. Kompilator widzi to Ai Bma relację podtypu, więc w widoku kodu wyrażenia wyrażenie może działać.

Kompilator nie bierze pod uwagę szczególnego przypadku, gdy dokładnie wie , jaki typ obiektu expression_of_type_Arzeczywiście będzie miał. Po prostu widzi typ statyczny jako Ai uważa, że ​​typem dynamicznym może być Adowolny potomek A, w tym B.


3

W takim przypadku, dlaczego Java pozwala na downcasting, jeśli nie można go wykonać w czasie wykonywania?

Wierzę, że dzieje się tak, ponieważ kompilator nie ma możliwości sprawdzenia w czasie kompilacji, czy rzutowanie się powiedzie, czy nie. Na przykład łatwo zauważyć, że rzutowanie zakończy się niepowodzeniem, ale w innych przypadkach nie jest to tak jasne.

Na przykład wyobraź sobie, że wszystkie typy B, C i D rozszerzają typ A, a następnie metoda public A getSomeA()zwraca instancję B, C lub D w zależności od losowo wygenerowanej liczby. Kompilator nie może wiedzieć, który dokładnie typ wykonania zostanie zwrócony tą metodą, więc jeśli później rzutujesz wyniki B, nie ma sposobu, aby dowiedzieć się, czy rzutowanie zakończy się powodzeniem (czy niepowodzeniem). Dlatego kompilator musi założyć, że rzutowanie się powiedzie.


2

@ Oryginalny plakat - patrz komentarze w tekście.

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

2

Downcast działa w przypadku, gdy mamy do czynienia z upcastowanym obiektem. Upcasting:

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

Więc teraz to objValuezmienna może być zawsze downcasted się int, ponieważ obiekt, który został odlany jest Integer,

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

ale ponieważ objValuejest Przedmiotem, na który nie można go rzucić, Stringponieważ intnie można go rzucić String.


0

Downcasting jest bardzo przydatny w poniższym fragmencie kodu, którego używam cały czas. W ten sposób udowodniono, że downcasting jest przydatny.

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

Przechowuję ciąg na liście połączonej. Kiedy pobieram elementy listy połączonej, obiekty są zwracane. Aby uzyskać dostęp do elementów jako ciągów (lub dowolnych innych obiektów klasy), downcasting pomaga mi.

Java pozwala nam skompilować kod downcast, ufając nam, że robimy coś złego. Jeśli jednak ludzie popełnią błąd, zostanie on złapany w czasie wykonywania.


Korzystanie z kolekcji innych niż ogólne w Javie jest odpowiednikiem void*wskaźników w C ++. To nie brzmi dla mnie jak dobry pomysł.
Jezor

0

Rozważ poniższy przykład

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

tutaj tworzymy obiekt podklasy Bone i przypisujemy go do referencji superklasy AOne, a teraz referencja superklasy nie wie o metodzie method2 w podklasie, tj. Bone w czasie kompilacji. dlatego musimy sprowadzić to odwołanie superklasy do referencji podklasy, tak aby wynikowe odniesienie może wiedzieć o obecności metod w podklasie, tj. kości


AOne wygląda na nieco zagmatwanego.
Zastanów się

0

Aby przeprowadzić downcasting w Javie i uniknąć wyjątków w czasie wykonywania, zapoznaj się z następującym kodem:

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

Tutaj Animal jest klasą nadrzędną, a Dog jest klasą podrzędną.
instanceof jest słowem kluczowym używanym do sprawdzania, czy zmienna referencyjna zawiera dany typ referencji do obiektu, czy nie.


0

Transformacja obiektów w dół nie jest możliwa. Tylko

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

jest możliwe

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

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.