Rozdział 4... 

Każdy jest innym i nikt sobą samym.

♦ Odpowiedniki konstrukcji języka C
93
została zainicjowana danymi koła, wszystko może się zdarzyć. Nawet, gdy unia z dyskryminatorem zostanie prawidłowo zainicjowana, możliwe jest omyłkowe przekazanie jej do funkcji nieodpowiedniej dla danej wartości znacznika.
Drugą zaletą hierarchii klas jest łatwość rozszerzania, nawet o wiele niezależnie działa-jących części. Aby rozszerzyć hierarchię klas, wystarczy dodać nową kasę pochodną.
Jeżeli zapomnisz zdefiniować jednej z metod abstrakcyjnych, natychmiast wskaże Ci to kompilator. Aby rozbudować unię z dyskryminatorem, należy mieć dostęp do kodu źródłowego. Musisz dodać nową wartość do typu oraz nową gałąź do instrukcji
w każdej funkcji operującej na unii. Na koniec musisz skompilować kod. Jeżeli w którejś funkcji zapomnisz dodać nowego przypadku, kompilator nie będzie w stanie tego wykryć. Pozostaje umieszczenie w kodzie kontroli niespodziewanych wartości znacznika i generowanie w takich sytuacjach komunikatów błędów.
Czwartą zaletą hierarchii klas jest możliwość odwzorowania naturalnych relacji hierar-chicznych pomiędzy typami, co pozwala na zwiększenie elastyczności i lepszej kontroli typów w czasie kompilacji. Załóżmy, że do naszego oryginalnego przykładu chcemy
dodać obsługę kwadratów. W hierarchii klas możemy odwzorować fakt, że kwadrat jest specjalnym rodzajem prostokąta (zakładając, że oba są niezmienne):
;< !$
;< ! !
!!
! !
77#=>?. =.!
Przedstawiona hierarchia klas nie jest jedynym rozwiązaniem naszego problemu. Hierarchia ta powstała po podjęciu kilku decyzji, o których warto wspomnieć. Klasy w hierarchii, poza , udostępniają swoje pola, nie oferując metod dostępowych. W przypadku klas publicznych jest to nie do zaakceptowania, ale nam zależało na zwięzłości kodu (temat 19.). Klasy te są niezmienne. Czasami nie jest to najlepsze, jednak najczęściej właśnie takie rozwiązanie jest właściwe (temat 13.).
Ponieważ język Java nie zawiera konstrukcji , można uważać, że nie ma niebez-
pieczeństwa utworzenia unii z dyskryminatorem. Możliwe jest jednak napisanie kodu, który będzie posiadał te same wady. Jeżeli kiedykolwiek będziesz chciał napisać klasę z polem znacznikowym, należy pomyśleć o eliminacji pola znacznikowego przez modyfikację hierarchii klas.
Innym zastosowaniem konstrukcji w języku C, całkowicie niezwiązanym z uniami
z dyskryminatorem, jest możliwość oglądania wewnętrznej reprezentacji danych poprzez umyślne omijanie systemu typów. Metoda ta demonstrowana jest przez poniższy fragment kodu w języku C, który drukuje wewnętrzną postać liczby w postaci szesnastkowej:
94
Efektywne programowanie w języku Java
9
9@@AA1B5
"CD "9
Choć może być to użyteczne, szczególnie dla programistów systemowych, takie nieprzenośne zastosowanie nie ma odpowiednika w języku Java. Działanie takie nie może być dopuszczalne w języku, który gwarantuje bezpieczeństwo typów i nieomal izoluje programistów od wewnętrznej reprezentacji danych.
Pakiet zawiera metody pozwalające przekształcić liczby zmiennoprzecin-
kowe na ich bitową reprezentację, ale działanie tych metod jest bardzo dokładnie zde-finiowane w celu zapewnienia ich przenośności. Poniższy fragment kodu jest luźnym odpowiednikiem przedstawionego kodu w języku C, ale gwarantuje uzyskanie iden-tycznych wyników bez względu na platformę, na której jest uruchomiony:
; # , E; F ', G @@AA1B5
Temat 21. Zastępowanie konstrukcji enum
za pomocÄ… klas
Konstrukcja również nie została przeniesiona do języka Java. Konstrukcja ta służy do definiowania typu wyliczeniowego — typu, składającego się ze stałego zbioru
wartości. Niestety, konstrukcja ta nie jest zbyt zaawansowana. Definiuje ona tylko zbiór nazwanych stałych typu , nie zapewniając żadnego mechanizmu kontroli typów. W języku C można wykonać następujące wyrażenia:
! #FHI,,,)*$())-;4,'E- 70!9JJ8:07
! #)(K%+'%4+%G+LL/ - 70!9J#M907
- #F ,,)7049 J8:9#M9#07
ale takie jest nieprawidłowe:
- FHI,1,,)7'%4+%
Konstrukcja nie zawiera przestrzeni nazw dla tworzących ją stałych. Dlatego poniż-
sza deklaracja, zawierająca użytą już nazwę, pozostaje w konflikcie z deklaracją typu
:
! #G+LL/;N%(''%($; !-
Typy definiowane za pomocą konstrukcji są niepewne. Dodanie stałych do takiego typu bez ponownej kompilacji klientów powoduje nieprzewidywalne działanie, niezależnie od tego, jak dokładnie są sprawdzane istniejące wartości stałych. Poszcze-gólne zespoły nie mogą niezależnie dodawać stałych do tych typów, ponieważ nowe
typy wyliczeniowe bardzo często są ze sobą w konflikcie. Konstrukcja nie zapewnia żadnego mechanizmu, ułatwiającego zamianę stałych wyliczanych na ciągi znaków lub przeglądanie stałych w typie.
Rozdział 4. ♦ Odpowiedniki konstrukcji języka C
95
Niestety, najczęściej spotykany sposób emulowania typu wyliczeniowego w języku Java posiada wszystkie wady konstrukcji z języka C:
!" ###
&!
;H,'-&+HG;6
;H,'-/,(4L)/;5
;H,'-E%($';O
;H,'-;(/%;P
Możesz się również spotkać z odmianą tego wzorca, wykorzystującą stałe typu .