• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Rzutowanie w Java

Object Storage Arubacloud
+2 głosów
1,487 wizyt
pytanie zadane 10 sierpnia 2016 w Java przez itcloud Gaduła (3,380 p.)
edycja 10 sierpnia 2016 przez itcloud

Temat rzutowania jest dla mnie oczywisty, jeśli chodzi o typy podstawowe. Np. rzutowanie z typu double do int (tracimy część ułamkową).

Natomiast pogubiłem się w przypadku rzutowania pomiędzy typami obiektów (nadklasy, podklasy itp.)

Przykład z książki, którą obecnie studiuję:

Manager boss = new Manager(...);  //tworzony jest boss typu kierownika

Employee[] staff = new Employee[3]; //tworzona jest tablica zespołu składającego się z trzech pracowników

staff[0] = boss;  //tutaj typ kierownika wkładam do pierwszej szufladki tablicy o typie pracownika (mogę, bo kierownik jest też jednocześnie pracownikiem

Zakładamy, że pozostałe dwie szufladki będą przechowywały obiekty typu pracownik (Emloyee).

No i teraz w dalszej części książki, przy okazji rzutowania:

Manager boss = (Manager) staff[1];   //Błąd

Manager boss = (Manager) staff[0]; //tak być może

Manager jest podklasą klasy Employee. Czemu więc to na czerwono jest błędne? W staff[1] trzymany jest obiekt typu Employee i samo staff[1] jest referencją do typu Employee. Nie można tak robić, bo pracownik nie może być zrzutowany na kierownika? Trochę tu się pogubiłem. Niby to potrafię opisać jak wyżej, ale nie czuję tego.

Tak samo jak z tym przykładem:

Date c = (Date) staff[1];

2 odpowiedzi

+1 głos
odpowiedź 10 sierpnia 2016 przez Porcupine Nałogowiec (31,560 p.)
wybrane 10 sierpnia 2016 przez itcloud
 
Najlepsza

Z prostego powodu: każdy manager jest pracownikiem, ale nie każdy pracownik jest managerem. Oznacza to, że skoro Manager dziedziczy po Employee to w jakiś sposób go rozszerza - oferuje coś czego normalny pracownik nie jest w stanie zrealizować. Rzutując pracownika do Managera próbujesz go zmusić żeby umiał robić to co robi Manager, a on biedny tego nie umie... 

Inny przykład: 
 

class Animal {
    // whatever
}


class Bird extends Animal {

    public void fly() {
        System.out.println("I'm flying!");
    }
}

// ...

Animal[] animals = new Animal[2];
animals[0] = new Animal();
animals[1] = new Bird();      // wszystko ok, bo każdy ptak jest zwierzęciem, wiadomo, że
                                            // ptak będzie mógł nam zapewnić to co zwierze potrafi

Bird bird = (Bird) animals[0];  // ClassCastException :( 
                                               // nie pyknie. Czemu? Bo chcesz jakiegoś biednego zwierzaka
                                               // zmusić do latania, a on może nie umieć

 

komentarz 10 sierpnia 2016 przez itcloud Gaduła (3,380 p.)
edycja 10 sierpnia 2016 przez itcloud

Czyli dodając moje komentarze::

animals[0] = new Animal(); //tworzę obiekt typu animal i zapisuję w tablicy obiektów animals (zwierzęta)

Bird bird = (Bird) animals[0]; //tutaj próbuję zamienić zwierzę na typ ptatak - rozumiem, czemu nie ma prawa zadziałać

I teraz: czy to powyższe jest równoznaczne z takim działaniem ?

Bird bird = new Animal();

Chyba tak, bo jak w swoim kodzie próbowałem:

Manager kierownik2 = new Employee();

To od razu wyskoczył błąd "type mismatch()". - czyli że nie każdy pracownik może być kierownikiem. Teraz dopiero zajarzyłem wszystko :)

 

Już wiem, co było przyczyną, że nie czaiłem tego z ksiązki:

Manager boss = (Manager) staff[0]; //tak być może

Jak pisałem wcześniej, staff było typu Employee i do środka tej tablicy były wkładane obiekty różnych typów (czyli typ w typie). Więc dzięki temu że do staff[0] włożono obiekt typu Manager, to.. no właśnie, nie rozumiałem tej konwersji. Że co podlega konwersji, to co w staff[0] czy również ten wycinek tej tablicy (komórka 0 która była referencją do typu Employee).

komentarz 10 sierpnia 2016 przez itcloud Gaduła (3,380 p.)

Poza tym nie wiem czy w książce nie ma błędu, bo:

Manager boss = (Manager) staff[1];   //błąd

(w staff[1] jest sobie w środku obiekt klasy Employee).

A niżej przykład inny, do czego zmierzam:

Date c = (Date) staff[1];

spowoduje błąd kompilacji, ponieważ Date nie jest podklasą klasy Employee.

No i jak to zacząłem analizować to zonk: bo przecież w tym u góry przykładzie (biorąc analogię do ostatniego przykłady z Date) - obiekt manager jest podklasą klasy Employee!

1
komentarz 10 sierpnia 2016 przez Porcupine Nałogowiec (31,560 p.)

obiekt manager jest podklasą

Tak, dlatego w przypadku Managera i Employee otrzymamy błąd wykonania, a nie błąd kompilacji. Ponieważ wyciągając z tablicy Employee i rzutując do (Manager) istnieje szansa (tak jak w przypadku:

Manager boss = (Manager) staff[0]; //tak być moż

, że wszystko pójdzie dobrze i rzutowanie się uda, bo faktycznie obiektem, który rzutujesz będzie Manager.

Z kolei Date c = (Date) staff[1]; pada na etapie kompilacji. Bo kompilator jest na tyle mądry, że od razu zauważy, że coś nie pykło, bo rzutujesz obiekt na drugi, podczas gdy nie mają one ze sobą nic wspólnego.

+2 głosów
odpowiedź 10 sierpnia 2016 przez MichuDev Pasjonat (20,300 p.)
  • Warto zaznajomić się ze słowem kluczowym instanceof. Dzięki niemu możemy sprawdzić typ Obiektu:
    Object[] array = new Object[2];
    array[0] = "Hello World";
    array[1] = 25;
    array[0] instanceof String; // true
    array[1] instanceof String; // false this is Integer
    

    Jak widać możemy sprawdzić typ obiektu który dziedziczy z danej klasy.

  • Nie można rzutować z klasy bazowej na podklasę gdy nie jest ona tym typem. Błąd ten nie jest błędem kompilacji! Przy sprawdzeniu typu przez instanceof mamy taką pewność.

komentarz 10 sierpnia 2016 przez itcloud Gaduła (3,380 p.)
edycja 10 sierpnia 2016 przez itcloud
>Nie można rzutować z klasy bazowej na podklasę gdy nie jest ona tym typem.

jakim typem? To instanceof znam, ale chciałbym nie musieć z niego korzystać, tylko intuicyjnie to pojąć. Jak masz doświadczenie, korzystasz z tego:

if(staff[1] instanceof Manager) { boss = (Manager) staff[1];}

Jak to się ma do tego co wyżej zaznaczyłem na czerwono? Chyba mógłbym to intuicyjnie sam rozstrzygnąć? Może inaczej zadam pytanie: jak rzutuję typy podstawowe, wszystko rozumiem, wiem kiedy stracę część informacji itp. A przy tym rzutowaniu typów własnych (obiekty danego typu rzutowane na innego typu) to już tego za bardzo nie czuję.
komentarz 10 sierpnia 2016 przez MichuDev Pasjonat (20,300 p.)

Tu chodzi o to, że w twoim programie Manager dziedziczy z Employee, więc gdy utworzysz obiekt klasy Manager, a dasz referencje do Employee to rzutowanie się powiedzie do klasy Manager się powiedzie. W przypadku gdy utworzysz Obiekt typu Employee to nie powiedzie się rzutowanie, zostanie rzucony wyjątek: https://docs.oracle.com/javase/7/docs/api/java/lang/ClassCastException.html.

komentarz 10 sierpnia 2016 przez itcloud Gaduła (3,380 p.)
"W przypadku gdy utworzysz Obiekt typu Employee to nie powiedzie się rzutowanie,..."

rzutowanie do podklasy? Możesz to opisać, jak rozumiesz tego typu operację, co robi komputer/kompilator? Może to mi coś wyjaśni. W przypadku typów podstawowych wszystko rozumiem.
1
komentarz 10 sierpnia 2016 przez MichuDev Pasjonat (20,300 p.)

Każdy obiekt w tym języku jest traktowany jako referencja do niego to oznacza, że w 2 i więcej miejscach programista może się odwoływać do danej zmiennej. W 1 miejscu może być odwołanie do typu String a w innym Object ponieważ String dziedziczy z Object, a typ obiektu to String.  Dzięki referencji do klasy bazowej w tym wypadku Object możemy wywołać na nim wszystkie metody klasy Object. Błędy rzutowania są tylko na etapie wykonania kodu.

Więc co robi JVM (Java Virtual Machine):

  • W danym miejscu jest referencja (wskaźnik) do danych. Java w ten sposób sprawdza typ Obiektu. Jeśli typ na który rzutujemy jest tym typem lub jest typem klasy bazowej lub implementowanego interfejsu możemy rzutować.
  • W przeciwnym wypadku rzucony jest wyjątek.
komentarz 10 sierpnia 2016 przez itcloud Gaduła (3,380 p.)
Aha, czyli przy typach prostych, wbudowanych nie ma tego problemu, bo wszystkie dziedziczą z klasy object? Tzn. int a Integer (obiekt) to co innego, ale zawsze kojarzyłem rzutowanie obiektu danego typu do innego typu własnie z rzutowaniem typów prostych i tu chyba tkwił mój błąd.
1
komentarz 10 sierpnia 2016 przez MichuDev Pasjonat (20,300 p.)
int,byte,short,float,double,boolean,char to typy proste, które nie dziedziczą z klasy Object, ale posiadają wersje obiektowe: Integer,Byte,Short,Float,Double,Boolean,Char które dziedziczą albo z klasy Object albo z Number, w  zależności czy to jest liczba, typ logiczny czy znak.

Podobne pytania

0 głosów
3 odpowiedzi 12,472 wizyt
pytanie zadane 9 stycznia 2016 w Java przez Int_main Bywalec (2,850 p.)
0 głosów
1 odpowiedź 370 wizyt
pytanie zadane 16 sierpnia 2016 w C i C++ przez Zeroche Obywatel (1,030 p.)
0 głosów
0 odpowiedzi 1,103 wizyt

92,575 zapytań

141,425 odpowiedzi

319,650 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...