Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
Dodatkowo niedoświadczeni użytkownicy mogą wbudować
stałe w kod zamiast korzystania z odpowiednich nazw pól. Jeżeli taka stała posiada błąd (literówkę), to błąd ten nie będzie wykryty w czasie kompilacji i będzie powodował
powstanie błędów wykonania.
Na szczęście język Java pozwala na utworzenie innej metody emulacji typu , która nie posiada wszystkich wad użycia poprzedniej metody z wartościami lub , a ponadto ma kilka dodatkowych zalet. Jest ona nazywana bezpiecznym typem wyliczeniowym. Typ ten nie jest niestety zbyt dobrze znany. Pomysł jest prosty — należy zdefiniować klasę, reprezentującą pojedynczy element typu wyliczeniowego, nie definiując publicznego konstruktora. Zamiast tego należy udostępnić publiczne pola statyczne typu , po jednym dla każdej ze stałych typu wyliczeniowego. Wzorzec ten w najprostszej postaci wygląda następująco:
;
; #
; ; # # #
; ; #
; &+HG; .; " "
; /,(4L)/; .; "!# !"
; E%($'; .; " "
; ;(/%; .; "!"
Ponieważ klienci nie mogą utworzyć obiektów tej klasy ani jej rozszerzać, nie mogą istnieć inne obiekty tego typu poza udostępnianymi przez pola statyczne. Choć klasa nie jest zadeklarowana jako , nie można po niej dziedziczyć — konstruktor klasy pochodnej musi wywołać konstruktor klasy bazowej, a on jest niedostępny.
Jak można się domyślić na podstawie nazwy, ten wzorzec pozwala na sprawdzanie typów w czasie kompilacji. Jeżeli zadeklarujesz metodę z parametrem typu , masz pewność, że każda referencja różna od będzie prawidłowym obiektem, reprezentującym jedną ze stałych. Wszystkie próby przekazania obiektu o niewłaściwym typie zostaną wykryte w czasie kompilacji, podobnie jak próby przypisania wyrażenia
96
Efektywne programowanie w języku Java
jednego typu wyliczeniowego do zmiennej innego typu. Można tworzyć wiele typów
wyliczeniowych z identycznie nazywającymi się stałymi, ponieważ każda klasa posiada swoją przestrzeń nazw.
Do takiej reprezentacji typu wyliczeniowego można dodawać kolejne stałe i nie powoduje to konieczności rekompilacji klientów, ponieważ publiczne statyczne pola, zawierające referencje do stałych, izolują klientów od klasy, realizującej typ wyliczeniowy. Same stałe nie są wkompilowywane w kod klienta, tak jak często zdarza się to w przypadku realizacji konstrukcji za pomocą rozwiązania, korzystającego z pól lub .
Ponieważ przedstawiona realizacja typu wyliczeniowego jest zwykłą klasą, może ona przedefiniować metodę , co pozwoli na zmianę wartości zmiennych na postać
nadającą się do wydrukowania. Jeżeli jest to potrzebne, można również zwracać komunikaty w odpowiednim języku. Zwróć uwagę, że ciągi znaków są wykorzystywane jedynie przez metodę . Nie są używane do porównywania obiektów, ponieważ
odziedziczona po klasie metoda porównuje referencje do obiektów.
Można również dodać do klasy implementującej typ wyliczeniowy dowolne metody,
jakie mogą być potrzebne. W naszej klasie może przydać się metoda zwracająca kolor lub rysunek, skojarzony z odpowiednią stałą. Klasa była początkowo prostą realizacją typu wyliczeniowego i z czasem zaczęła się zamieniać w bogatą w funkcję abstrakcję opisywanego obiektu.
Ponieważ do klasy implementującej typ wyliczeniowy można dodawać dowolne metody, może ona implementować interfejsy. Na przykład załóżmy, że chcemy, aby nasza
klasa implementowała interfejs , co pozwoli klasom sortować karty
według koloru. Przedstawimy teraz modyfikację oryginalnego wzorca, która imple-mentuje ten interfejs. Zmienna statyczna !" używana jest do przypisywania numeru kolejnego dla każdego z tworzonych obiektów. Numery te są używane przez
metodę # do porządkowania obiektów.
; ##
; #
77) #:J .9 :
L! 6
779 # :J !:
! L! QQ
; ; # # #
; ; #
#'LJ
! 1; !
; &+HG; .; " "
; /,(4L)/; .; "!# !"
; E%($'; .; " "
; ;(/%; .; "!"
Rozdział 4. ♦ Odpowiedniki konstrukcji języka C
97
Ponieważ stałe typu wyliczeniowego są obiektami, można umieszczać je w kolekcjach.
Załóżmy na przykład, że chcemy w klasie udostępnić niezmienną listę kolorów w standardowym porządku. Wystarczy dodać do klasy deklarację dwóch klas pól.
; RS$,K('%-K(+H%;
&+HG;/,(4L)/;E%($';;(/%;
K(+H%;
& #!+ (+ $,K('%-K(+H%;
W przeciwieństwie do najprostszej postaci typu wyliczeniowego klasy w wersji korzystającej z numerów kolejnych mogą być serializowane (rozdział 9.) z zachowaniem szczególnej ostrożności. Nie wystarczy dodać do deklaracji klasy klauzuli
$ . Wymagane jest również utworzenie metody " (temat 57.).
LJ !$ .LJ ; #%
$,K('%-K(+H%;R! S77T# >: 9 U
Metoda ta, wywołana automatycznie przez system serializacji, zabezpiecza przed powie-laniem istniejących stałych, powstałych w procesie deserializacji. Zapewnia ona, że będzie istniał tylko jeden obiekt dla każdej stałej typu wyliczeniowego, co pozwala uniknąć konieczności przedefiniowywania metody . Bez tej gwarancji metoda
będzie zwracała nieprawidłowe wyniki, jeżeli będzie porównywała dwie
równe stałe, ale o różnych kolejnych numerach. Zwróć uwagę, że metoda "