Wyliczenie w Javie - po co używać toString zamiast name

171

Jeśli spojrzysz w enum api na metodę name(), mówi ona, że:

Zwraca nazwę tej stałej wyliczeniowej, dokładnie taką, jaka została zadeklarowana w deklaracji wyliczenia. Większość programistów powinna używać metody toString zamiast tej, ponieważ metoda toString może zwracać bardziej przyjazną dla użytkownika nazwę. Ta metoda jest przeznaczona głównie do stosowania w specjalnych sytuacjach, w których poprawność zależy od uzyskania dokładnej nazwy, która nie będzie się różnić w zależności od wydania.

Dlaczego lepiej używać toString()? Chodzi mi o to, że toString może zostać zastąpione, gdy name () jest już ostateczna. Więc jeśli użyjesz toString i ktoś nadpisze to, aby zwrócić zakodowaną na stałe wartość, cała aplikacja jest wyłączona ... Również jeśli spojrzysz na źródła, metoda toString () zwraca dokładnie i tylko nazwę. To jest to samo.

spauny
źródło
4
Możesz nadpisać toString()w swoim wyliczeniu, ale nikt inny nie może go rozszerzyć ani zastąpić. Nie możesz przedłużyć wyliczeń.
Erick G. Hagstrom

Odpowiedzi:

198

To naprawdę zależy od tego, co chcesz zrobić ze zwróconą wartością:

  • Jeśli chcesz uzyskać dokładną nazwę używaną do zadeklarowania stałej wyliczeniowej, powinieneś użyć nazwy, name()która toStringmogła zostać zastąpiona
  • Jeśli chcesz wydrukować stałą wyliczenia w sposób przyjazny dla użytkownika, powinieneś użyć tego, toStringktóry mógł zostać zastąpiony (lub nie!).

Kiedy czuję, że może to być mylące, podaję bardziej szczegółową getXXXmetodę, na przykład:

public enum Fields {
    LAST_NAME("Last Name"), FIRST_NAME("First Name");

    private final String fieldDescription;

    private Fields(String value) {
        fieldDescription = value;
    }

    public String getFieldDescription() {
        return fieldDescription;
    }
}
asylias
źródło
1
To jest do zaakceptowania. Chociaż gdyby pozwolili enumowi mieć klasę bazową, byłoby łatwiej.
ralphgabb
55

Użyj, name()jeśli chcesz dokonać porównania lub użyć wartości zakodowanej na stałe do wewnętrznego użytku w kodzie.

Używaj, toString()gdy chcesz przedstawić informacje użytkownikowi (w tym programistom przeglądającym dziennik). Nigdy nie polegaj w swoim kodzie na toString()nadaniu określonej wartości. Nigdy nie testuj go z określonym ciągiem. Jeśli twój kod zepsuje się, gdy ktoś poprawnie zmieni toString()zwrot, to już był uszkodzony.

Z javadoc (wyróżnienie moje):

Zwraca ciąg znaków reprezentujący obiekt. Ogólnie metoda toString zwraca łańcuch, który „tekstowo reprezentuje” ten obiekt. Wynik powinien być zwięzły, ale pouczający i łatwy do odczytania przez osobę . Zaleca się, aby wszystkie podklasy zastępowały tę metodę.

Denys Séguret
źródło
23

name()jest „wbudowaną” metodą enum. To jest ostateczne i nie możesz zmienić jego realizacji. Zwraca nazwę stałej wyliczeniowej w takiej postaci, w jakiej jest zapisana, np. Dużymi literami, bez spacji itp.

Porównaj MOBILE_PHONE_NUMBERi Mobile phone number. Która wersja jest bardziej czytelna? Wierzę w to drugie. Na tym polega różnica: name()zawsze zwraca MOBILE_PHONE_NUMBER, toString()może zostać zastąpiona wartością powrotu Mobile phone number.

AlexR
źródło
16
To nie jest poprawne!! Funkcja toString () zwraca Mobile phone numbertylko wtedy, gdy przesłonisz ją w celu zwrócenia takiej wartości. W przeciwnym razie powróciMOBILE_PHONE_NUMBER
spauny
2
@spayny, to oczywiste, że musisz nadpisać toString(). Haczyk polega na tym, że name()nie można go przesłonić, ponieważ jest ostateczny.
AlexR
13
@AlexR Być może będziesz musiał uwzględnić powyższy komentarz w odpowiedzi, aby był w 100% poprawny
artfullyContrived
5
Zgadzam się z @artfullyContrived, ponieważ nie było to dla mnie oczywiste, dopóki nie przeczytałem twoich komentarzy.
martinatime
13

Chociaż większość ludzi ślepo postępuje zgodnie z radą javadoc, istnieją bardzo specyficzne sytuacje, w których faktycznie chcesz uniknąć metody toString (). Na przykład używam wyliczeń w moim kodzie Java, ale muszą one zostać serializowane do bazy danych iz powrotem. Gdybym używał metody toString (), technicznie rzecz biorąc, podlegałbym nadpisaniu zachowania, jak wskazali inni.

Dodatkowo można również deserializować z bazy danych, na przykład powinno to zawsze działać w Javie:

MyEnum taco = MyEnum.valueOf(MyEnum.TACO.name());

Ale nie jest to gwarantowane:

MyEnum taco = MyEnum.valueOf(MyEnum.TACO.toString());

Nawiasem mówiąc, wydaje mi się bardzo dziwne, że Javadoc wyraźnie mówi „większość programistów powinna”. Znajduję bardzo mało przypadków użycia w toString enum, jeśli ludzie używają tego dla "przyjaznej nazwy", jest to wyraźnie kiepski przypadek użycia, ponieważ powinni używać czegoś bardziej kompatybilnego z i18n, co w większości przypadków oznaczałoby użyj metody name ().

kodowanie
źródło
2
Dzięki za odpowiedź. Minęło prawie 5 lat, odkąd zadałem to pytanie, a nie używałem jeszcze metody toString () do wyliczenia! 99% przypadków używam wyliczeń dokładnie do tego, co opisałeś: serializacji i deserializacji nazw wyliczeń do / z bazy danych.
spauny
5

Praktycznym przykładem, gdy name () i toString () mają sens, są różne, jest wzorzec, w którym wyliczenie jednowartościowe jest używane do definiowania singletona. Z początku wygląda to zaskakująco, ale ma dużo sensu:

enum SingletonComponent {
    INSTANCE(/*..configuration...*/);

    /* ...behavior... */

    @Override
    String toString() {
      return "SingletonComponent"; // better than default "INSTANCE"
    }
}

W takim wypadku:

SingletonComponent myComponent = SingletonComponent.INSTANCE;
assertThat(myComponent.name()).isEqualTo("INSTANCE"); // blah
assertThat(myComponent.toString()).isEqualTo("SingletonComponent"); // better
Marcin
źródło
Tak, masz rację ... dobrym przykładem są predykaty wyliczenia
Guawy
4

name () to dosłownie nazwa tekstowa w kodzie java wyliczenia. Oznacza to, że jest ograniczony do ciągów, które mogą faktycznie pojawić się w kodzie java, ale nie wszystkie pożądane ciągi można wyrazić w kodzie. Na przykład możesz potrzebować ciągu zaczynającego się od liczby. name () nigdy nie będzie w stanie uzyskać tego ciągu.

Intropy
źródło
-1

Możesz także użyć czegoś takiego jak poniższy kod. Użyłem lomboka, aby uniknąć pisania niektórych standardowych kodów dla getterów i konstruktora.

@AllArgsConstructor
@Getter
public enum RetroDeviceStatus {
    DELIVERED(0,"Delivered"),
    ACCEPTED(1, "Accepted"),
    REJECTED(2, "Rejected"),
    REPAIRED(3, "Repaired");

    private final Integer value;
    private final String stringValue;

    @Override
    public String toString() {
        return this.stringValue;
    }
}
Kamyar Miremadi
źródło