Java枚举-为什么使用toString代替名称


171

如果您在枚举api中查看该方法,name()则会显示:

返回此枚举常量的名称,该名称与在其枚举声明中声明的完全相同。大多数程序员应该优先使用toString方法,因为toString方法可能返回更用户友好的名称。此方法主要设计用于在特殊情况下正确性取决于获得确切名称的特殊情况,具体名称在发行版本之间不会有所不同。

为什么更好用toString()?我的意思是当name()已经是final时,toString可能会被覆盖。因此,如果您使用toString且有人重写它以返回硬编码值,则整个应用程序都将关闭...同样,如果您查看源代码,则toString()方法也将仅返回名称。这是同一件事。


4
您可以toString()在枚举中覆盖,但没有其他人可以扩展和覆盖它。您不能扩展枚举。
Erick G. Hagstrom

Answers:


198

这实际上取决于您要对返回值执行的操作:

  • 如果需要获取用于声明枚举常量的确切名称,则应使用,name()因为它toString可能已被覆盖
  • 如果要以用户友好的方式打印枚举常量,则应使用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;
    }
}

1
这是可以接受的。尽管如果让枚举具有基类,那么事情会更容易。
ralphgabb

55

使用name()时要作一比较,或使用硬编码值代码中的一些内部使用。

toString()当您想向用户(包括查看日志的开发人员)展示信息时使用。切勿依赖您的代码来toString()提供特定的值。切勿针对特定字符串进行测试。如果您的代码在有人正确更改toString()返回值时中断,则说明它已经中断。

从javadoc(重点是我的):

返回对象的字符串表示形式。通常,toString方法返回一个“以文本形式表示”此对象的字符串。结果应该是简洁易懂的表示形式,使人易于阅读建议所有子类都重写此方法。


23

name()是的“内置”方法enum。这是最终的,您不能更改其实现。它返回写入时的枚举常量的名称,例如大写字母,不带空格等。

比较MOBILE_PHONE_NUMBERMobile phone number。哪个版本更具可读性?我相信第二个。区别在于:name()始终返回MOBILE_PHONE_NUMBERtoString()可以被覆盖以返回Mobile phone number


16
这是不正确的!toString()Mobile phone number仅在覆盖它以返回该值时返回。否则,它会返回MOBILE_PHONE_NUMBER
笨拙的2012年

2
@spayny,很明显,您必须重写toString()。渔获量是name()不能改变的,因为它是最终的。
AlexR 2012年

13
@AlexR,您可能需要将您的上述评论纳入答案中,以使其100%正确
巧妙地设计为

5
我同意@artfullyContrived,因为在我阅读您的评论之前,这对我来说并不明显。
martinatime 2014年

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%的时间完全按照您的描述使用枚举:将枚举名称与数据库进行序列化和反序列化。
spauny

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

是的,您是对的...一个很好的例子是番石榴的枚举谓词
笨拙的2016年

4

name()实际上是枚举的Java代码中的文本名称。这意味着它限于实际上可以出现在您的Java代码中的字符串,但并非所有所需的字符串都可以在代码中表示。例如,您可能需要以数字开头的字符串。name()将永远无法为您获取该字符串。


-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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.