Java pytanie rekrutacyjne: Metody typu wyliczeniowego

Wymień metody typu wyliczeniowego i opisz krótko ich przeznaczenie

W typie wyliczeniowym znajdziemy następujące metody:

  1. name(),
  2. ordinal(),
  3. values(),
  4. valueOf(String name).

Przypuśćmy, że mamy typ wyliczeniowy Gender:

package org.blog;

public enum Gender {
    MALE,
    FEMALE
}

name()

Metoda zwraca nazwę obiektu, czyli wywołując:

System.out.println(Gender.MALE.name());

Otrzymamy:

MALE

ordinal()

Ze względu, że każdy obiekt typu wyliczeniowego ma przypisany swój numer to możemy go odczytać właśnie przy użyciu metody ordinal().

Czyli taki kod:

System.out.println(Gender.FEMALE.ordinal());

Zwróci nam:

1

Otrzymaliśmy 1, ponieważ numeracja rozpoczyna się klasycznie jak to bywa w programowaniu od 0.

values()

Metoda values() zwraca nam tablicę obiektów danego typu wyliczeniowego.

Gender [] genders = Gender.values();

Dla pewności możemy je wyświetlić:

for (Gender gender: genders) {
    System.out.println(gender);
}

A wtedy otrzymamy:

MALE
FEMALE

valueOf(String name)

Metoda valueOf umożliwia nam otrzymanie obiektu typu wyliczeniowego na podstawie nazwy w postaci Stringa.

Gender male = Gender.valueOf("MALE");

Jednak trzeba pamiętać, że wielkość liter ma znaczenie. Przy takim wywołaniu:

Gender maleLowerCase = Gender.valueOf("male");

Otrzymamy wyjątek IllegalArgumentException:

Exception in thread "main" java.lang.IllegalArgumentException: No enum constant org.blog.Gender.male

Który informuje nas, że nie można znaleźć obiektu dla podanej nazwy.

toString()

O ile metoda toString jest domyślnie w każdym obiekcie (pochodzi z klasy Object) to typie wyliczeniowym ma ona domyślnie inne zachowanie niż w każdym innym obiekcie.

Normalny obiekt bez przeciążonej metody toString() zwróci nam m.in. swój adres w pamięci:

org.blog.Main$User@6d6f6e28

Za to typ wyliczeniowy zwróci swoją nazwę – tak jak metoda name():

System.out.println(Gender.MALE.toString());

Dla potwierdzenie efekt w konsoli:

MALE

Bonus

Metody name i ordinal są finalne, tzn. że nie można ich przysłonić. Łatwo to sprawdzić.

package org.blog;

public enum Gender {
    MALE,
    FEMALE;

    @Override
    public String name() {
        return super.name().toLowerCase();
    }

    @Override
    public int ordinal() {
        return super.ordinal() + 1;
    }
}

Metody values() oraz valueOf(String name) są statyczne, więc jest oczywiste, że nie mamy możliwości ich przysłonięcia.

Kompilując powyższy kod otrzymamy taki błąd:

Error:(11, 19) java: name() in org.blog.Gender cannot override name() in java.lang.Enum
  overridden method is final

Error:(16, 16) java: ordinal() in org.blog.Gender cannot override ordinal() in java.lang.Enum
  overridden method is final

Czasami potrzebujemy w inny sposób parsować Stringa na dany obiekt typu wyliczeniowego, a już wiemy, że przysłonięcie tej metody jest niemożliwe. Jak sobie można się z tym uporać?

Bezpośrednio w danym enumie można zaimplementować statyczną metodę, np.:

public static Gender getGenderByValue(String value) {
    String valueLowerCase = value.toLowerCase();

    return Arrays.asList(values()).stream()
            .filter(gender -> gender.name().toLowerCase().equals(valueLowerCase))
            .findFirst()
            .orElseThrow(IllegalArgumentException::new);
}

Mając napisaną taką metodę możemy już się nie martwić wielkością znaków:

System.out.println(Gender.getGenderByValue("male"));

Jedynym problemem jest to, że nigdy nie możemy wymusić używania tej metody zamiast valueOf co w przyszłości może spowodać problemy w aplikacji.