Перечисление Java - зачем использовать toString вместо имени

171

Если вы посмотрите в enum api на метод, name()он говорит, что:

Возвращает имя этой константы перечисления, в точности как объявлено в объявлении перечисления. Большинство программистов должны использовать метод toString вместо этого, поскольку метод toString может возвращать более удобное для пользователя имя. Этот метод предназначен в первую очередь для использования в специализированных ситуациях, где правильность зависит от получения точного имени, которое не будет меняться от выпуска к выпуску.

Почему лучше использовать toString()? Я имею в виду, что toString может быть переопределено, когда name () уже окончательно. Так что, если вы используете toString, и кто-то переопределяет его, чтобы вернуть жестко запрограммированное значение, все ваше приложение не работает ... Также, если вы посмотрите в исходных текстах, метод toString () возвращает точно и только имя. Это то же самое.

spauny
источник
4
Вы можете переопределить toString()в своем перечислении, но никто другой не может расширить и переопределить его. Вы не можете расширять перечисления.
Эрик Дж. Хагстром

Ответы:

199

Это действительно зависит от того, что вы хотите сделать с возвращаемым значением:

  • Если вам нужно получить точное имя, используемое для объявления константы enum, вы должны использовать, name()как, toStringвозможно, было переопределено
  • Если вы хотите напечатать константу enum удобным для пользователя способом, вам следует использовать то, toStringчто могло быть переопределено (или нет!).

Когда я чувствую, что это может сбить с толку, я предоставляю более конкретный getXXXметод, например:

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;
    }
}
assylias
источник
1
Это приемлемо Хотя, если они позволят enum иметь базовый класс, все будет проще.
Ральфгабб
55

Используйте, name()если вы хотите сделать сравнение или использовать жестко закодированное значение для внутреннего использования в вашем коде.

Используется, toString()когда вы хотите представить информацию пользователю (включая разработчика, просматривающего журнал). Никогда не полагайтесь в своем коде на toString()конкретное значение. Никогда не проверяйте это против определенной строки. Если ваш код ломается, когда кто-то корректно меняет toString()возврат, значит, он уже сломан.

От Javadoc (выделение мое):

Возвращает строковое представление объекта. В общем случае метод toString возвращает строку, которая «представляет собой текст» этого объекта. Результатом должно быть краткое, но информативное представление, которое легко читается человеком . Рекомендуется, чтобы все подклассы переопределяли этот метод.

Денис Сегюре
источник
23

name()это «встроенный» метод enum. Это является окончательным, и вы не можете изменить его реализацию. Возвращает имя константы enum в том виде, как оно написано, например, в верхнем регистре, без пробелов и т. Д.

Сравните MOBILE_PHONE_NUMBERи Mobile phone number. Какая версия более читабельна? Я верю второму. В этом разница: name()всегда возвращается MOBILE_PHONE_NUMBER, toString()может быть переопределена для возврата Mobile phone number.

AlexR
источник
16
Это не правильно !! toString () возвращает Mobile phone numberтолько если вы переопределите его, чтобы вернуть такое значение. В противном случае он вернетсяMOBILE_PHONE_NUMBER
Spauny
2
@ Spayny, очевидно, что вы должны переопределить toString(). Подвох в том, что name()его нельзя переопределить, так как он окончательный.
AlexR
13
@AlexR вам может понадобиться добавить вышеупомянутый комментарий в ответ, чтобы сделать его на 100% правильным
artfullyContrived
5
Я согласен с @artfullyContrived, так как это не было очевидно для меня, пока я не прочитал ваши комментарии.
martinatime
13

Хотя большинство людей слепо следуют советам javadoc, есть очень специфические ситуации, когда вы хотите избежать toString (). Например, я использую перечисления в своем коде Java, но они должны быть сериализованы в базу данных и обратно. Если бы я использовал toString (), то технически я был бы подвержен переопределенному поведению, как указывали другие.

Кроме того, можно также десериализовать из базы данных, например, это всегда должно работать в Java:

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

Принимая во внимание, что это не гарантируется:

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

Кстати, для Javadoc было бы очень странно прямо сказать «большинство программистов должны». Я нахожу очень маленький вариант использования в toString перечисления, если люди используют его для «дружественного имени», которое явно плохой вариант использования, поскольку они должны использовать что-то более совместимое с i18n, что в большинстве случаев используйте метод name ().

кодирование
источник
2
Спасибо за ответы. Прошло почти 5 лет с тех пор, как я задал этот вопрос, и мне еще предстоит использовать метод toString () для перечисления! 99% случаев я использую перечисления именно для того, что вы описали: сериализацию и десериализацию имен перечислений в / из базы данных.
спауны
5

Практический пример, когда name () и toString () имеют смысл различаться, - это шаблон, в котором однозначное перечисление используется для определения синглтона. Поначалу это выглядит удивительно, но имеет большой смысл:

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

    /* ...behavior... */

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

В таком случае:

SingletonComponent myComponent = SingletonComponent.INSTANCE;
assertThat(myComponent.name()).isEqualTo("INSTANCE"); // blah
assertThat(myComponent.toString()).isEqualTo("SingletonComponent"); // better
Marcin
источник
Да, вы правы ... хороший пример - список предикатов
Гуавы
4

name () - это буквально текстовое имя в java-коде перечисления. Это означает, что он ограничен строками, которые могут фактически появляться в вашем Java-коде, но не все желаемые строки могут быть выражены в коде. Например, вам может понадобиться строка, которая начинается с цифры. name () никогда не сможет получить эту строку для вас.

Intropy
источник
-1

Вы также можете использовать что-то вроде кода ниже. Я использовал lombok, чтобы избежать написания некоторых шаблонных кодов для геттеров и конструкторов.

@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;
    }
}
Камьяр Миремади
источник