Nauka programowania Java #14: Typ wyliczeniowy

Poznałeś/aś już dwa rodzaje typów referencyjnych: obiekt i tablicę. Dziś zajmę się kolejnym z nich, czyli typem wyliczeniowym.  Typ wyliczeniowy to specjalny rodzaj klas, które z miejsca stają się klasami niezmiennymi (immutable). Jak pamiętasz obiekt takiej klasy raz stworzony nie podlega później zmianie. Jest to szczególnie ważne w środowisku wielowątkowym (czyli przy obecnych wieloprocesorowych komputerach praktycznie zawsze  😉 ) . Dlatego warto poznać enumy i korzystać z nich od czasu do czasu.

Podstawowe użycie enumów jest jednak trochę inne. W swoich początkowych programach będziesz ich używał ze względu na wygodę. Typ wyliczeniowy przydaje się, gdy chcesz żeby Twój kod był bardziej czytelny. Zademonstruję Ci teraz dwa przykłady, kiedy użycie enumów jest użyteczne.

public class SimpleEnum {
  public enum CounterEnum {
    FIRST, SECOND, THIRD;
  }
  
  public static void main(String [] args) {
    for(CounterEnum item: CounterEnum.values()) {
      if (item.toString().equals("FIRST")) {
        System.out.println("OK");
      } else {
        System.out.println("WRONG");
      }
    }
  }
}

W tym przykładzie napisałem bardzo prosty typ wyliczeniowy, składający się z trzech wartości: FIRST, SECOND i THIRD. Wypisałem je po przecinku w pierwszej linii kodu. Ostatnia wartość została zamknięta znakiem średnika. Tak zaprogramowany typ wyliczeniowy mogę teraz użyć w kodzie. Robi się to w ten sam sposób jak przy wykorzystaniu metod lub pól statycznych. Pamiętasz pewnie, że są one dostępne dla programisty bez tworzenia obiektu (wywołuje się je po wpisaniu nazwy klasy i użyciu separatora kropki). Na przykład, aby odwołać się do wartości FIRST napisałbym: CounterEnum.FIRST. Każdy enum dostarcza nam kilka przydatnych metod, jedną z nich jest metoda values, która zwraca wszystkie wartości typu w postaci tablicy. Dzięki temu można w łatwy sposób przeiterować po wszystkich wartościach w enumie. Iteracja została wykonana pętlą for each, która różni się trochę od pętli for. Nie korzysta się w niej z indeksów, lecz z zaimplementowanego iteratora. W przypadku enumów nie musisz go implementować, wystarczy że pętla zapiszesz w sposób następujący:

for(NazwaEnuma nazwaWłasnaKolejnejWartościEnuma : TablicaEnumówUtworzonaPrzezMetodęValues) {
// wykonane obliczenia
}

W moim przypadku było to: for(CounterEnum item: CounterEnum.values())

Teraz mogę też wykonać jakąś logikę. Załóżmy, że chciałbym aby po każdym kliknięciu na przycisk na stronie www wyświetał mi się napisać OK, jeśli będzie to button FIRST lub WRONG jeśli będzie to SECOND lub THIRD. Porównuję wyrażeniem if-else wartość enuma konwertowaną za pomocą metody toString() do typu String z jakimś sygnałem z zewnątrz (w tym przypadku jest to parametr clickedButton) i wyświetlami odpowiedni napis. Prawda, że kod wygląda dużo ładniej, niż gdybym użył standardowej tablicy Stringów?

Jest jeszcze jeden dobry powód, aby używać Enumów. Enumy to świetna ‘przechowalnia’ stałych. Przykładowo, powielasz w kilku klasach napis ‘TYTUŁ’ i zgodnie z zasadą ‘Don’t repeat yourself’ piwinieneś unikać powielania kodu. Dlaczego więc nie wrzucić takie stałe do enuma?

public class ConstantsEnum {
  public enum MyEnum {
    OK("ok");
    private final String name;
    
    private MyEnum(String name) {
      this.name= name;
    }
    
    public String getName() {
      return name;
    }
  }
  
  public static void main(String [] args) {
    System.out.println(MyEnum.OK.getName());
    System.out.println(MyEnum.OK);
    MyEnum[] values = MyEnum.values();
    for (MyEnum myEnum : values) {
      System.out.println(myEnum.getName());
    }
  }
}

Ten typ wyliczeniowy jest bardziej skomplikowany. Jak widzisz enum może posiadać, tak jak klasa: metody, pola i konstruktory. Do metod i pól odwołujesz się przez nazwę enuma (tak jak przy użyciu słowa static w klasie). Możesz używać wszelkich modyfikatorów dostępu takich jak w przypadku klas. Jedynym wyjątkiem jest konstruktor, którego dostęp zawsze musi być publiczny lub pakietowy. Aby przypisać wartość do typu wyliczeniowego należy otworzyć okrągły nawias i wpisać tam porządaną wartość. Następnie jesteś zobowiązany/a do napisania kontruktora z parametrem, którego typ będzie tożsamy z typem, który używać będziesz w nawiasach (np. String). Dodatkowo, aby mieć dostęp do tej wartości utworzyłem pole wraz z akcesorem dostępu (metoda getName()).

Teraz można użyć takiego typu wyliczeniowego w metodzie main.

Dodaj komentarz