• Nie Znaleziono Wyników

Konwersje typów prostych

N/A
N/A
Protected

Academic year: 2021

Share "Konwersje typów prostych"

Copied!
12
0
0

Pełen tekst

(1)

Polimorfizm

Monika Wrzosek (IM UG) Programowanie obiektowe 137 / 148

(2)

Konwersje typów prostych

i n t a = 9 / 2 ; // k o n w e r s j a a u t o m a t y c z n a ; w y n i k : 4 i n t b = (i n t) 9 / 2 // k o n w e r s j a j a w n a ; w y n i k : 4

Poniższy program zakończy się błędem kompilacji

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { f l o a t f = 0 ;

i n t i = f ; // b ł ą d ! e r r o r : i n c o m p a t i b l e t y p e s }

}

Jeśli zmiennej, która reprezentuje węższy zakres wartości (np. int),

przypisuje się zmienną mogącą przedstawiać szerszy zakres wartości (np. float), trzeba zawsze dokonać jawnej konwersji typu (o ile jest ona możliwa):

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { f l o a t f = 0 ;

i n t i = (i n t) f ; //OK

} }

Monika Wrzosek (IM UG) Programowanie obiektowe 138 / 148

(3)

Konwersje typów prostych

Sytuacja odwrotna:

zmiennej typu float (reprezentującej szerszy zakres wartości) próbujemy przypisać wartość zmiennej typu int (reprezentującej węższy zakres wartości).

Wykonana zostanie konwersja automatyczna:

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { i n t i = 5 ;

f l o a t f = i ; //OK

} }

Instrukcję

f l o a t f = i ;

kompilator potraktuje tak, jakby miała ona postać:

f l o a t f = (f l o a t) i ;

Monika Wrzosek (IM UG) Programowanie obiektowe 139 / 148

(4)

Rzutowanie typów obiektowych

Jeśli oczekiwany jest argument klasy X, a podany zostanie argument klasy Y, która jest klasą potomną dla X, błędu nie ma (konwersja automatyczna typu obiektu).

p u b l i c c l a s s Punkt { p u b l i c i n t x , y ; }

p u b l i c c l a s s Punkt3D e x t e n d s Punkt { p u b l i c i n t z ;

}

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Punkt P ;

P = new Punkt3D ( ) ;

P . x = 2 ; //OK

P . y = 3 ; //OK

P . z = 5 ; //B ł ą d !

} }

Dla kompilatora typem zmiennej P jest Punkt, a w klasie Punkt nie ma pola z.

Monika Wrzosek (IM UG) Programowanie obiektowe 140 / 148

(5)

Rzutowanie typów obiektowych

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Punkt P ;

P = new Punkt3D ( ) ;

P . x = 2 ; //OK

P . y = 3 ; //OK

P . z = 5 ; //B ł ą d !

} }

Instrukcja:

P = new Punkt3D ( ) ;

jest przez kompilator rozumiana jako:

P = ( Punkt ) new Punkt3D ( ) ;

Przypomina to konwersje typów prostych, jednak znaczenie jest nieco inne.

Jest to informacja dla kompilatora:

traktuj obiekt klasy Punkt3D tak, jakby był on klasy Punkt.

Obiekt Punkt3D nie zmienia się ani nie traci żadnych informacji.

Monika Wrzosek (IM UG) Programowanie obiektowe 141 / 148

(6)

Rzutowania można dokonać również na zmiennych już istniejących, np.:

Punkt3D punkt3D = new Punkt3D ( ) ; Punkt p u n k t = ( Punkt ) punkt3D ;

To również dowód, że dokonuje się tu rzutowania typów, a nie konwersji.

Przypomnijmy konwersję typów prostych:

f l o a t f = 4 . 5 ; i n t i = (i n t) f ;

Część ułamkowa zostanie tu bezpowrotnie utracona i nawet powtórna konwersja na typ float nie przywróci poprzedniej wartości:

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { f l o a t f = 4 . 5 ;

i n t i = (i n t) f ; f l o a t f f = (f l o a t) i ;

S y s t e m . o u t . p r i n t l n (" f = " + f ) ; S y s t e m . o u t . p r i n t l n (" i = " + i ) ; S y s t e m . o u t . p r i n t l n (" f f = " + f f ) ; }}

f = 4 . 5

i = 4

f f = 4 . 0

W przypadku zmiennych obiektowych będzie zupełnie inaczej.

Monika Wrzosek (IM UG) Programowanie obiektowe 142 / 148

(7)

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Punkt3D p3D1 = new Punkt3D ( ) ;

p3D1 . x = 1 ; p3D1 . y = 2 ; p3D1 . z = 3 ; S y s t e m . o u t . p r i n t l n (" p3D1 : ") ;

S y s t e m . o u t . p r i n t l n (" x = " + p3D1 . x ) ; S y s t e m . o u t . p r i n t l n (" y = " + p3D1 . y ) ; S y s t e m . o u t . p r i n t l n (" z = " + p3D1 . z ) ; S y s t e m . o u t . p r i n t l n (" ") ;

Punkt p u n k t = ( Punkt ) p3D1 ; S y s t e m . o u t . p r i n t l n (" p u n k t ") ;

S y s t e m . o u t . p r i n t l n (" x = " + p u n k t . x ) ; S y s t e m . o u t . p r i n t l n (" y = " + p u n k t . y ) ; S y s t e m . o u t . p r i n t l n (" ") ;

Punkt3D p3D2 = ( Punkt3D ) p u n k t ; S y s t e m . o u t . p r i n t l n (" p3D2 ") ;

S y s t e m . o u t . p r i n t l n (" x = " + p3D2 . x ) ; S y s t e m . o u t . p r i n t l n (" y = " + p3D2 . y ) ; S y s t e m . o u t . p r i n t l n (" z = " + p3D2 . z ) ; }}

p3D1 : x = 1 y = 2 z = 3

p u n k t : x = 1 y = 2

p3D2 : x = 1 y = 2 z = 3

Widzimy, że rzutowanie nie zmienia stanu obiektu. Konwersje nie spowodowały utraty części danych.

Monika Wrzosek (IM UG) Programowanie obiektowe 143 / 148

(8)

Klasa Object

W Javie wszystkie klasy dziedziczą bezpośrednio lub pośrednio po klasie Object.

Jeśli definicja nowej klasy bazowej wygląda następująco

p u b l i c c l a s s n a z w a _ k l a s y {

}

kompilator potraktuje ten fragment jako:

p u b l i c c l a s s n a z w a _ k l a s y e x t e n d s O b j e c t { }

Zatem każda klasa dziedziczy wszystkie metody i pola klasy Object.

Metoda toString zwraca opis obiektu w postaci ciągu znaków (formalnie: w postaci obiektu klasy String).

p u b l i c c l a s s M o j a K l a s a {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { M o j a K l a s a mk = new M o j a K l a s a ( ) ;

S y s t e m . o u t . p r i n t l n (mk ) ; }

}

Przykładowy wynik:

MojaKlasa@15db9742

Monika Wrzosek (IM UG) Programowanie obiektowe 144 / 148

(9)

Rzeczywisty typ obiektu

Rzutowanie obiektów jest możliwe w obie strony:

- obiekt klasy potomnej można rzutować na obiekt klasy bazowej (rzutowanie w górę), - obiekt klasy bazowej można rzutować na obiekt klasy potomnej (rzutowanie w dół) - bardziej skomplikowane.

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Punkt p = new Punkt ( ) ;

Punkt3D p3D = ( Punkt3D ) p ;

p3D . z = 5 ; //B ł ą d 1

p3D . x = 5 ; //B ł ą d 2

}}

Błąd 1 - oczywisty, obiekt klasy Punkt nie ma pola z.

Błąd 2 - nieoczywisty, przecież obiekt na który wskazuje zmienna p3D, zawiera pole x.

To jednak nie ma znaczenia, bo sam obiekt jest innej klasy niż Punkt3D.

Maszyna wirtualna sprawdza zgodność klas. Zmienna wskazująca na obiekt musi być tej samej klasy lub klasy nadrzędnej do klasy tego obiektu - nigdy odwrotnie.

Polimorfizm/późne wiązanie/dynamiczne wiązanie (ang. late/dynamic binding ) Sprawdzanie rzeczywistego (a nie deklarowanego) typu obiektu w trakcie działania programu.

W przypadku rzutowania w dół polimorfizm uniemożliwia wykonanie niedozwolonych operacji. O wiele bardziej użyteczny jest jednak przy rzutowaniu w górę.

Monika Wrzosek (IM UG) Programowanie obiektowe 145 / 148

(10)

p u b l i c c l a s s M o j a K l a s a { p u b l i c S t r i n g t o S t r i n g ( ) {

r e t u r n " M o j a K l a s a "; }}

p u b l i c c l a s s M o j a D r u g a K l a s a e x t e n d s M o j a K l a s a { p u b l i c S t r i n g t o S t r i n g ( ) {

r e t u r n " M o j a D r u g a K l a s a "; }}

p u b l i c c l a s s M o j a T r z e c i a K l a s a e x t e n d s M o j a K l a s a { p u b l i c S t r i n g t o S t r i n g ( ) {

r e t u r n " M o j a T r z e c i a K l a s a "; }}

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { M o j a K l a s a mk1 = new M o j a D r u g a K l a s a ( ) ; M o j a K l a s a mk2 = new M o j a T r z e c i a K l a s a ( ) ; S y s t e m . o u t . p r i n t l n ( mk1 . t o S t r i n g ( ) ) ; S y s t e m . o u t . p r i n t l n ( mk2 . t o S t r i n g ( ) ) ; }}

M o j a D r u g a K l a s a M o j a T r z e c i a K l a s a

Monika Wrzosek (IM UG) Programowanie obiektowe 146 / 148

(11)

p u b l i c c l a s s Main {

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { M o j a K l a s a mk1 = new M o j a D r u g a K l a s a ( ) ; M o j a K l a s a mk2 = new M o j a T r z e c i a K l a s a ( ) ; S y s t e m . o u t . p r i n t l n ( mk1 . t o S t r i n g ( ) ) ; S y s t e m . o u t . p r i n t l n ( mk2 . t o S t r i n g ( ) ) ; }

}

W Javie wywołania metod są domyślnie polimorficzne, tzn. skojarzenie obiektu z metodą jest wykonywane w trakcie działania programu. Sprawdzeniu podlega rzeczywisty typ obiektu, którego metoda jest wywoływana. Ponieważ rzeczywistym typem dla mk1 jest MojaDrugaKlasa, a dla mk2 - MojaTrzeciaKlasa, na ekranie pojawi się:

M o j a D r u g a K l a s a M o j a T r z e c i a K l a s a

Gdyby wiązanie (czyli skojarzenie wywołania metody z obiektem) odbywało się już na etapie kompilacji (wczesne wiązanie, ang. early binding ), wynik byłby inny.

W trakcie kompilacji mk1 i mk2 są dla kompilatora obiektami klasy MojaKlasa, więc przypisałby on im kod metody toString z klasy MojaKlasa.

Dzięki wywołaniom polimorficznym wiązanie następuje dopiero w trakcie działania programu i pod uwagę jest brany rzeczywisty typ obiektu. To jedna z podstawowych zasad programowania obiektowego.

Monika Wrzosek (IM UG) Programowanie obiektowe 147 / 148

(12)

Polimorfizm a metody prywatne

Przy wykorzystywaniu polimorficznych wywołań metod należy pamiętać, że mają one miejsce, kiedy metoda X z klasy bazowej jest przesłaniana przez metodę X z klasy potomnej.

Problem może wystąpić, kiedy w klasie bazowej zostanie zdefiniowana metoda prywatna. Taka metoda nie może być przesłonięta (bo przecież metody prywatne są dostępne wyłącznie dla klasy, w której zostały zdefiniowane).

Jednak to, że dana metoda została zadeklarowana w klasie bazowej jako prywatna, nie oznacza wcale, że nie można zdefiniować metody o takiej samej nazwie i argumentach w klasie pochodnej. Nie zaleca się jednak stosowania tego typu konstrukcji, gdyż zaciemnia to sposób działania kodu.

Monika Wrzosek (IM UG) Programowanie obiektowe 148 / 148

Cytaty

Powiązane dokumenty

Składowe publiczne klasy bazowej są odziedziczone jako publiczne, a składowe chronione jako chronione.. Dziedziczenie chronione - składowe publiczne są dziedziczone jako

n Dla obiektu, którego klasy nie można jednoznacznie określić na etapie kompilacji, odwołania do metody, bądź metod zadeklarowanych jako wirtualne będą się odbywały

Na końcu tej funkcji umieszczamy wiersze: system("pause"); - polecenie to zatrzymuje wykonanie programu do momentu naciśnięcia jakiegoś klawisza (pozwala to zobaczyć

• defualt – dostęp pakietowy - klasy z tego samego pakietu (biblioteki) mają domyślny dostęp do składowych klasy nieoznaczonych odpowiednim słowem kluczowym...

Za pomocą klas programista stara się opisać obiekty, ich właściwości, zbudować konstrukcje, interfejs, dzięki któremu będzie można wydawać polecenia realizowane potem

W klasie Main i metodzie main utwórz obiekt klasy Taxi i wyświetl na ekranie średni przebieg i średnie zarobki. Monika Wrzosek (IM UG) Programowanie obiektowe 17

Takie rozwiązanie Jest wygodne, ale może być niebezpieczne, zwłaszcza jeśli mieszanie typów jest wynikiem błędu programisty..

obiekty przechowywane w kontenerze musz¡ by¢ porównywalne wstawianie elementów odbywa si¦ zawsze w przeznaczonym miejscu zale»nym od stanu kontenera. wewn¦trznie implementowany