Jaka jest różnica między rzutowaniem w górę a rzutowaniem w dół w odniesieniu do zmiennej klasy


138

Jaka jest różnica między rzutowaniem w górę a rzutowaniem w dół w odniesieniu do zmiennej klasy?

Na przykład w poniższym programie klasa Animal zawiera tylko jedną metodę, ale klasa Dog zawiera dwie metody, a następnie sposób rzutowania zmiennej Dog na zmienną Animal Variable.

Jeśli rzucanie jest zakończone, to jak możemy nazwać inną metodę Dog za pomocą zmiennej Animal.

class Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}


class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class UseAnimlas 
{
    public static void main (String [] args) 
    {
        Dog d = new Dog();      
        Animal a = (Animal)d;
        d.callme();
        a.callme();
        ((Dog) a).callme2();
    }
}

A Dogjest Animal. W większości przypadków wysyłanie w górę jest niepotrzebne, chyba że chcesz użyć określonej przeciążonej metody. callmeistnieje w obu Animali Dog. callme2istnieje tylko w Dog, który rzucasz asię Dog, aby to działało.
Brian

Jaki jest wynik twojego kodu?
Malwinder Singh

Ciekawostką jest to, że d.callme zwraca „In callme of Dog”, chociaż d został rzucony na zwierzę !!
Chris311

4
@ Chris311 zarówno „d”, jak i „a” wskazują na ten sam obiekt ... który jest psem, ale „a” ma dostęp tylko do metod specyficznych dla Dog, gdy jest wyłączony w czasie wykonywania. W rzeczywistości: zwierzę a = (zwierzę) d; jest niepotrzebne, potrzebujesz tylko Animal a = d; kiedy się podniecasz.
Mark Keen,

Odpowiedzi:


223

Przesyłanie w górę jest rzutowaniem do nadtypu, podczas gdy przesyłanie w dół jest rzutowaniem do podtypu. Upcasting jest zawsze dozwolony, ale downcasting wymaga sprawdzenia typu i może wyrzucić ClassCastException.

W twoim przypadku rzut z a Dogdo an Animalto upcast, ponieważ a Dogis-a Animal. Ogólnie rzecz biorąc, możesz upcastować, ilekroć istnieje relacja między dwiema klasami.

Downcasting wyglądałby mniej więcej tak:

Animal animal = new Dog();
Dog castedDog = (Dog) animal;

Zasadniczo co robisz jest mówienie kompilator, że wiesz co typu runtime obiektu naprawdę jest. Kompilator zezwoli na konwersję, ale nadal wstawi test poprawności w czasie wykonywania, aby upewnić się, że konwersja ma sens. W tym przypadku rzutowanie jest możliwe, ponieważ w czasie wykonywania animaljest faktycznie a, Dogmimo że statyczny typ animalto Animal.

Jeśli jednak miałbyś to zrobić:

Animal animal = new Animal();
Dog notADog = (Dog) animal;

Dostałbyś ClassCastException. Powodem jest to, że animaltyp środowiska wykonawczego to Animal, więc kiedy powiesz środowisku wykonawczemu, aby wykonał rzutowanie, widzi, że animaltak naprawdę nie jest Doga, więc rzuca ClassCastException.

Aby wywołać metodę nadklasy, możesz wykonać super.method()lub wykonać upcast.

Aby wywołać metodę podklasy, musisz wykonać downcast. Jak pokazano powyżej, ClassCastExceptionrobiąc to zwykle ryzykujesz ; jednak możesz użyć instanceofoperatora, aby sprawdzić typ środowiska uruchomieniowego obiektu przed wykonaniem rzutowania, co pozwala zapobiec ClassCastExceptions:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog) {
    // Guaranteed to succeed, barring classloader shenanigans
    Dog castedDog = (Dog) animal;
}

Czy odpowiednie obniżenie zapewnia nie, ClassCastExceptionczy nie? Jak w pierwszym przypadku?
Malwinder Singh

@MS Co masz na myśli mówiąc „właściwe”?
awksp

2
@awksp To jest doskonała i wyraźna odpowiedź. Prawie podsumowuje wszystko, co muszę wiedzieć o castingu.
Gautham Honnavara

na pewno nie zrobiłeś zajęć, na których zwierzę jest przykładem psa, prawda? więc dlaczego w ogóle to sprawdzasz?
barlop

62

Down-casting i up-casting wyglądał następująco:
wprowadź opis obrazu tutaj

Upcasting : Kiedy chcemy przesłać klasę Sub do klasy Super, używamy Upcasting (lub poszerzania). Dzieje się to automatycznie, nie trzeba nic robić wprost.

Downcasting : Kiedy chcemy przesłać klasę Super do klasy Sub, używamy Downcasting (lub zawężania), a Downcasting nie jest bezpośrednio możliwy w Javie, wyraźnie musimy to zrobić.

Dog d = new Dog();
Animal a = (Animal) d; //Explicitly you have done upcasting. Actually no need, we can directly type cast like Animal a = d; compiler now treat Dog as Animal but still it is Dog even after upcasting
d.callme();
a.callme(); // It calls Dog's method even though we use Animal reference.
((Dog) a).callme2(); // Downcasting: Compiler does know Animal it is, In order to use Dog methods, we have to do typecast explicitly.
// Internally if it is not a Dog object it throws ClassCastException

Więc nie ma takiego sposobu na wywołanie metody upcastingowej, aby wywołać rodzica metody?
karlihnos,

32

Upcasting i downcasting są ważną częścią Javy, która pozwala nam budować skomplikowane programy przy użyciu prostej składni i daje nam ogromne korzyści, takie jak polimorfizm lub grupowanie różnych obiektów. Java pozwala na traktowanie obiektu typu podklasy jako obiektu dowolnego typu nadklasy. Nazywa się to upcastingiem. Upcasting jest wykonywany automatycznie, podczas gdy downcasting musi być wykonywany ręcznie przez programistę , a ja postaram się wyjaśnić, dlaczego tak się dzieje.

Upcasting i downcasting NIE są jak rzutowanie prymitywów z jednego na drugi i uważam, że to właśnie powoduje wiele zamieszania, gdy programista zaczyna uczyć się rzucania obiektów.

Polimorfizm: wszystkie metody w Javie są domyślnie wirtualne. Oznacza to, że każda metoda może zostać zastąpiona, gdy jest używana w dziedziczeniu, chyba że ta metoda jest zadeklarowana jako ostateczna lub statyczna .

Możesz zobaczyć poniższy przykład, jak getType();działa w zależności od typu obiektu (Pies, Zwierzę domowe, Pies policyjny).

Załóżmy, że masz trzy psy

  1. Pies - to jest super klasa.

  2. Pet Dog - Pet Dog przedłuża psa.

  3. Police Dog - Police Dog przedłuża Pet Dog.

    public class Dog{ 
       public String getType () {
          System.out.println("NormalDog");
          return "NormalDog";
       }
     }
    
    /**
     * Pet Dog has an extra method dogName()
     */   
    public class PetDog extends Dog{ 
       public String getType () {
          System.out.println("PetDog");
          return "PetDog";
       }
       public String dogName () {
          System.out.println("I don't have Name !!");
          return "NO Name";
       }
     }
    
    /**
     * Police Dog has an extra method secretId()
     */
    public class PoliceDog extends PetDog{
    
     public String secretId() {
        System.out.println("ID");
        return "ID";
     }
    
     public String getType () {
         System.out.println("I am a Police Dog");
         return "Police Dog";
     }
    }

Polimorfizm: wszystkie metody w Javie są domyślnie wirtualne. Oznacza to, że każda metoda może zostać zastąpiona, gdy jest używana w dziedziczeniu, chyba że ta metoda jest zadeklarowana jako ostateczna lub statyczna. (Wyjaśnienie należy do pojęcia wirtualnych tabel).

Virtual Table / Dispatch Table: Tabela wysyłkowa obiektu będzie zawierać adresy dynamicznie powiązanych metod obiektu. Wywołania metod są wykonywane przez pobranie adresu metody z tabeli wysyłkowej obiektu. Tablica wysyłki jest taka sama dla wszystkich obiektów należących do tej samej klasy i dlatego jest zwykle między nimi współdzielona.

public static void main (String[] args) {
      /**
       * Creating the different objects with super class Reference
       */
     Dog obj1 = new Dog();
`         /**
           *  Object of Pet Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry about it 
           *  
           */
     Dog obj2 = new PetDog();
`         /**
           *  Object of Police Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry       
           *  about it here even though we are extending PoliceDog with PetDog 
           *  since PetDog is extending Dog Java automatically upcast for us 
           */
      Dog obj3 = new PoliceDog();
}



 obj1.getType();

Wydruki Normal Dog

  obj2.getType();

Wydruki Pet Dog

 obj3.getType();

Wydruki Police Dog

Programista musi ręcznie wykonać downcasting

Podczas próby wywołania secretID();metody, do obj3której istnieje PoliceDog objectodniesienie, do Dogktórej jest superklasa w hierarchii, zgłasza błąd, ponieważ obj3nie ma dostępu do secretId()metody. Aby wywołać tę metodę, musisz ręcznie Downcast do tego obj3 PoliceDog

  ( (PoliceDog)obj3).secretID();

który drukuje ID

W podobny sposób, aby wywołać dogName();metodę w PetDogklasie, do której musisz przejść obj2w PetDogdół, ponieważ odwołuje się do obj2 Dogi nie ma dostępu do dogName();metody

  ( (PetDog)obj2).dogName();

Dlaczego tak się dzieje, że upcasting jest automatyczny, a downcasting musi być ręczny? Cóż, widzisz, upcasting nigdy nie zawiedzie. Ale jeśli masz grupę różnych psów i chcą spuszczonymi ich wszystkich do ich typów, to jest szansa, że niektóre z tych psów są rzeczywiście różne typy tj PetDog, PoliceDogi proces nie powiedzie się, rzucając ClassCastException.

To jest powód, dla którego musisz ręcznie obniżyć swoje obiekty, jeśli utworzyłeś odniesienia do obiektów do typu superklasy.

Uwaga: tutaj przez odniesienie oznacza, że ​​nie zmieniasz adresu pamięci swoich ojectów, gdy je przesyłasz w dół, nadal pozostaje taki sam, w tym przypadku po prostu grupujesz je według określonego typu Dog


„Polimorfizm wykorzystuje automatyczne obniżenie podczas wywołań metod”. Nie, nie ma. Używany mechanizm nie jest określony, ale najczęściej stosowany mechanizm -a vtable - nic takiego nie robi. Zajrzyj do kodu wynikowego. Bez przygnębienia.
Markiz Lorne,

Dlaczego nie? Oto, co dzieje się dobrze, czy możesz podać przykład, gdzie to nie zadziała?
Nagarjuna Yelisetty

1
Dlaczego nie? Oto, co dzieje się dobrze… czy możesz podać przykład, w którym stwierdzenie „Polimorfizm używa automatycznego obniżania wartości podczas wywołań metod”. zawiedzie lub nie spełni się?
Nagarjuna Yelisetty

To twoja argumentacja. To do Ciebie należy udowodnienie tego. Pokaż, gdzie w kodzie obiektowym występuje downcast. Odpowiedź na pytanie „dlaczego nie” brzmi „ponieważ nie jest to konieczne”. Vtable zajmuje się wysyłką metody, a zmienna wskazuje już na cały obiekt.
Markiz Lorne

1
„O ile wiem, moje twierdzenia są prawdziwe i tak jest w każdym przypadku” nie jest dowodem. To zwykłe stwierdzenie. Pytam was , aby udowodnić swoje oświadczenia. Nie robisz tego. W efekcie tylko się powtarzasz. I przedstawiłem już kilka obaleń. Podałem też procedurę decyzyjną. Jeśli możesz znaleźć downcast w kodzie obiektowym wywołania metody, masz rację, a ja się mylę. Tak się robi nauka. Zrób to. Twierdzenie, że „odważnie polegam na dokumentacji”, jest rażącym błędnym przedstawieniem. Nie rób tego.
Marquis of Lorne

12

Wiem, że to pytanie zadane dość dawno temu, ale dla nowych użytkowników tego pytania. Przeczytaj ten artykuł, w którym znajduje się pełny opis upcastingu, downcastingu i korzystania z operatora instanceof

  • Nie ma potrzeby ręcznej aktualizacji, dzieje się to samo:

    Mammal m = (Mammal)new Cat(); równa Mammal m = new Cat();

  • Ale downcasting zawsze musi być wykonywany ręcznie:

    Cat c1 = new Cat();      
    Animal a = c1;      //automatic upcasting to Animal
    Cat c2 = (Cat) a;    //manual downcasting back to a Cat

Dlaczego tak się dzieje, że upcasting jest automatyczny, a downcasting musi być ręczny? Cóż, widzisz, upcasting nigdy nie zawiedzie. Ale jeśli masz grupę różnych zwierząt i chcesz sprowadzić je wszystkie na kota, istnieje szansa, że ​​niektóre z tych zwierząt są w rzeczywistości psami i proces się nie powiedzie, rzucając ClassCastException. W tym miejscu należy wprowadzić przydatną funkcję o nazwie „instanceof” , która sprawdza, czy obiekt jest instancją jakiejś klasy.

 Cat c1 = new Cat();         
    Animal a = c1;       //upcasting to Animal
    if(a instanceof Cat){ // testing if the Animal is a Cat
        System.out.println("It's a Cat! Now i can safely downcast it to a Cat, without a fear of failure.");        
        Cat c2 = (Cat)a;
    }

Aby uzyskać więcej informacji, przeczytaj ten artykuł


Słuszna uwaga: Mammal m = (Mammal) new Cat (); równa się Mammal m = new Cat ();
Catbuilts

6

Lepiej wypróbuj tę metodę upcastingu, łatwo to zrozumieć:

/* upcasting problem */
class Animal
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}

class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class Useanimlas 
{
    public static void main (String [] args) 
    {
        Animal animal = new Animal ();
        Dog dog = new Dog();
        Animal ref;
        ref = animal;
        ref.callme();
        ref = dog;
        ref.callme();
    }
}

aw ostatnim wierszu mogłoby to być: ((Dog) ref) .callme2 (); // do obniżania / zawężania i dostępu do metody Callme2 () klasy Dog.
udarH3

6

Może ten stół pomoże. Wywołanie callme()metody class Parentlub class Child. Z zasady:

WYKONANIE -> Ukrywanie

POBRANIE -> Odkrywanie

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj


4

1.- Nadawanie.

Wykonując upcasting, definiujesz tag pewnego typu, który wskazuje na obiekt podtypu (Typ i podtyp można nazwać klasą i podklasą, jeśli czujesz się bardziej komfortowo ...).

Animal animalCat = new Cat();

Co oznacza, że ​​taki tag, animalCat, będzie miał funkcjonalność (metody) tylko typu Animal, ponieważ zadeklarowaliśmy go jako typ Animal, a nie Cat.

Możemy to robić w sposób „naturalny / niejawny / automatyczny”, w czasie kompilacji lub w czasie wykonywania, głównie dlatego, że Cat dziedziczy część swoich funkcji po Animal; na przykład move (). (A przynajmniej kot to zwierzę, prawda?)

2.- Downcasting.

Ale co by się stało, gdybyśmy musieli uzyskać funkcjonalność Cat z naszego tagu typu Animal?

Ponieważ stworzyliśmy tag animalCat wskazujący na obiekt Cat, potrzebujemy sposobu na wywołanie metod obiektu Cat z naszego tagu animalCat w sprytny, ładny sposób.

Taka procedura jest tym, co nazywamy Downcastingiem i możemy to zrobić tylko w czasie wykonywania.

Czas na kod:

public class Animal {
    public String move() {
        return "Going to somewhere";
    }
}

public class Cat extends Animal{
    public String makeNoise() {
        return "Meow!";
    }   
}

public class Test {

    public static void main(String[] args) {
        
    //1.- Upcasting 
    //  __Type_____tag________object
        Animal animalCat = new Cat();
    //Some animal movement
        System.out.println(animalCat.move());
        //prints "Going to somewhere"
        
    //2.- Downcasting   
    //Now you wanna make some Animal noise.
        //First of all: type Animal hasn't any makeNoise() functionality.
        //But Cat can do it!. I wanna be an Animal Cat now!!
        
        //___________________Downcast__tag_____ Cat's method
        String animalNoise = ( (Cat) animalCat ).makeNoise();
        
        System.out.println(animalNoise);
        //Prints "Meow!", as cats usually done.
        
    //3.- An Animal may be a Cat, but a Dog or a Rhinoceros too.
        //All of them have their own noises and own functionalities.
        //Uncomment below and read the error in the console:
        
    //  __Type_____tag________object
        //Cat catAnimal = new Animal();
        
    }

}

2

Rodzic: Samochód
Dziecko: Figo
Car c1 = new Figo ();

=====
Upcasting: -
Metoda: Obiekt c1 będzie odnosił się do metod klasy (Figo - metoda musi zostać nadpisana), ponieważ klasa „Figo” jest określona jako „nowa”.
Zmienna instancji: Obiekt c1 będzie odnosił się do zmiennej instancji klasy deklaracji („samochód”).

Gdy klasa Declaration jest rodzicem i obiekt jest tworzony jako potomek, następuje rzutowanie niejawne, czyli „Upcasting”.

======
Downcasting: -
Figo f1 = (Figo) c1; //
Metoda: Obiekt f1 będzie odwoływał się do metody klasy (figo), ponieważ początkowy obiekt c1 jest tworzony z klasą „Figo”. ale po wykonaniu rzutu w dół metody, które są obecne tylko w klasie „Figo”, mogą być również odniesione przez zmienną f1.
Zmienna instancji: Obiekt f1 nie będzie odnosił się do zmiennej instancji klasy Deklaracja obiektu c1 (klasą deklaracji dla c1 jest CAR), ale przy rzutowaniu w dół będzie odnosił się do zmiennych instancji klasy Figo.

======
Użycie: Gdy obiekt jest klasy potomnej, a klasa deklaracji jest nadrzędna, a klasa podrzędna chce uzyskać dostęp do zmiennej instancji własnej klasy, a nie klasy nadrzędnej, można to zrobić za pomocą opcji „Downcasting”.


1

upcasting oznacza rzutowanie obiektu na nadtyp, podczas gdy downcasting oznacza rzutowanie na podtyp.

W Javie wysyłanie do góry nie jest konieczne, ponieważ jest wykonywane automatycznie. Zwykle nazywa się to rzutowaniem niejawnym. Możesz to określić, aby było jasne dla innych.

Tak więc pisząc

Animal a = (Animal)d;

lub

Animal a = d;

prowadzi do dokładnie tego samego punktu iw obu przypadkach zostanie wykonany callme()from Dog.

Zamiast tego konieczna jest redukcja, ponieważ zdefiniowałeś ajako obiekt zwierzęcia. Obecnie wiesz, że jest to plik Dog, ale java nie ma gwarancji, że tak jest. Właściwie w czasie wykonywania może być inaczej i java wyrzuci ClassCastException, czy tak się stanie. Oczywiście tak nie jest w przypadku twojego przykładowego przykładu. Jeśli nie chcesz przesyłać ado Animal, java nie może nawet skompilować aplikacji, ponieważ Animalnie ma metody callme2().

W przykładzie nie można dotrzeć kod callme()z Animalod UseAnimlas(bo Doggo zastąpić), chyba że metoda byłaby następująca:

class Dog extends Animal 
{ 
    public void callme()
    {
        super.callme();
        System.out.println("In callme of Dog");
    }
    ... 
} 

0

Możemy stworzyć obiekt do Downcasting. Również w tym typie. : wywołanie metod klasy bazowej

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
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.