Enumy w Kotlinie
To jest rozdział z książki Kotlin Essentials.
W tym rozdziale zapoznamy się z enumami, czyli specjalnym rodzajem klasy, służącym do reprezentowania stałego zbioru wartości. Zacznijmy od przykładu. Załóżmy, że implementujesz metodę płatności, która musi obsługiwać trzy możliwe opcje: płatność gotówką, płatność kartą i przelew bankowy. Najprostszym sposobem na reprezentowanie stałego zestawu wartości w Kotlinie jest enum0. W jego ciele wymieniamy wszystkie wartości, oddzielając je przecinkami. Wartości nazywamy przy użyciu notacji UPPER_SNAKE_CASE (np. CASH
). Do wartości klasy enum można odwołać się przez nazwę tej wartości, poprzedzoną nazwą klasy i kropką (np. PaymentOption.CASH
). Wszystkie te wartości mają typ enuma (w tym przypadku PaymentOption
).
Na każdej klasie reprezentującej enum możemy wywołać:
- właściwość
entries
, która zwraca listę wszystkich wartości tego enuma. Jest to nowoczesna wersja funkcjivalues
, która zwraca tablicę tych wartości3. - funkcję
valueOf
, która zamienia stringa na wartość o pasującej nazwie (z uwzględnieniem wielkości liter) lub rzuca wyjątek.
Możemy również użyć funkcji enumValues
i enumValueOf
, w których typ enuma określamy przy użyciu argumentu generycznego.
Jak widać, kolejność elementów w enumach jest ważna. Funkcje values
oraz enumValues
zawsze zwracają wartości w kolejności, w jakiej są one zdefiniowane. Warto też dodać, że każda wartość enuma ma dwie właściwości:
name
- nazwa tej wartości,ordinal
- pozycja tej wartości (zaczynając od 0).
Każdy enum jest podklasą abstrakcyjnej klasy Enum
. Ta nadrzędna klasa gwarantuje właściwości name
i ordinal
. Enumy mają właściwości, które implementują toString
, equals
oraz hashCode
, ale w przeciwieństwie do klas danych, mają także compareTo
(ich naturalna kolejność to kolejność definicji).
Enumy można używać w warunkach when. Co więcej, nie ma potrzeby używania gałęzi else, gdy uwzględniamy wszystkie możliwe wartości enuma.
Enumy są bardzo wygodne, ponieważ można je łatwo przetwarzać na ciągi znaków lub odczytywać. Są popularnym sposobem reprezentowania skończonego zestawu możliwych wartości.
Dane w wartościach wyliczeń
W Kotlinie każda wartość wyliczenia może przechowywać stan. Można zdefiniować konstruktor główny dla enuma, a następnie każda wartość musi określić swoje dane obok nazwy. Dobrą praktyką jest, aby wartości wyliczeń były zawsze niemutowalne, więc ich stan nigdy nie powinien ulegać zmianie.
Enumy z własnymi metodami
Enumy w Kotlinie mogą mieć abstrakcyjne metody, których implementacje są specyficzne dla elementu. Kiedy je definiujemy, enum musi zdefiniować abstrakcyjną metodę, a każdy element musi ją nadpisać:
Ta opcja nie jest popularna, ponieważ zazwyczaj wolimy używać funkcyjnych właściwości konstruktora głównego1 lub funkcji rozszerzających2.
Podsumowanie
Enumy to wygodny sposób reprezentowania konkretnego zestawu możliwych wartości. Każda wartość ma właściwości name
i ordinal
. Możemy uzyskać tablicę wszystkich wartości za pomocą funkcji statycznej values
lub funkcji enumValues
. Możemy także zamienić String
na enuma za pomocą funkcji statycznej valueOf
lub funkcji enumValueOf
.
W następnym rozdziale porozmawiamy o sealed klasach, które często traktowane są jako podobne do enumów, ale reprezentują zupełnie inną (potężniejszą) abstrakcję. Enumy reprezentują zestaw stałych wartości, podczas gdy sealed klasy mogą tworzyć zamkniętą hierarchię klas.
W polskiej literaturze często używa się nazwy wyliczenie zamiast enum, ale w tej książce będę używał angielskiej nazwy.
Zmienne funkcyjne są opisane w książce Funkcyjny Kotlin. Przykład użycia enuma z funkcjonalnymi właściwościami konstruktora głównego przedstawiłem w książce Efektywny Kotlin, Temat 41: Użyj wyliczenia do reprezentowania listy wartości.
Funkcje rozszerzeń są opisane później w tej książce.
Właściwość entries
została dodana w wersji 1.9, a więc w projektach używających starszych wersji Kotlina należy użyć values
zamiast entries
.