Java 枚举 - 为啥使用 toString 而不是 name

Posted

技术标签:

【中文标题】Java 枚举 - 为啥使用 toString 而不是 name【英文标题】:Java enum - why use toString instead of nameJava 枚举 - 为什么使用 toString 而不是 name 【发布时间】:2012-10-28 18:28:15 【问题描述】:

如果您在枚举 api 中查看 name() 方法,它会说:

返回此枚举常量的名称,与其枚举声明中的声明完全相同。 大多数程序员应该优先使用 toString 方法而不是这个方法, 因为 toString 方法可能会返回一个对用户更友好的名称。 此方法主要设计用于正确性取决于获取确切名称的特殊情况,该名称不会因版本而异。

为什么使用toString() 更好?我的意思是当 name() 已经是 final 时,可能会覆盖 toString。因此,如果您使用 toString 并且有人覆盖它以返回一个硬编码的值,那么您的整个应用程序就会关闭......此外,如果您查看源代码,则 toString() 方法会准确返回名称,并且只返回名称。都是一样的。

【问题讨论】:

你可以在你的枚举中覆盖toString(),但没有其他人可以扩展和覆盖它。你不能扩展枚举。 【参考方案1】:

当您想要进行比较时使用name() 或将硬编码值用于代码中的某些内部用途。

当您想向用户(包括查看日志的开发人员)呈现信息时,请使用toString()。永远不要在你的代码中依赖toString() 给出一个特定的值。切勿针对特定字符串对其进行测试。如果在有人正确更改 toString() 返回时您的代码中断了,那么它已经损坏了。

来自 javadoc(重点是我的):

返回对象的字符串表示形式。一般来说, toString 方法返回一个“以文本形式表示”的字符串 目的。结果应该是简洁但信息丰富的表示 这对于一个人来说很容易阅读建议所有 子类覆盖此方法。

【讨论】:

【参考方案2】:

这真的取决于你想对返回值做什么:

如果您需要获取用于声明枚举常量的确切名称,您应该使用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;
    

【讨论】:

这是可以接受的。虽然如果他们让 enum 有一个基类,事情会更容易。【参考方案3】:

name()enum 的“内置”方法。它是最终的,你不能改变它的实现。它在写入时返回枚举常量的名称,例如大写,没有空格等。

比较 MOBILE_PHONE_NUMBERMobile phone number。哪个版本更易读?我相信第二个。这就是区别:name() 总是返回 MOBILE_PHONE_NUMBERtoString() 可能被覆盖以返回 Mobile phone number

【讨论】:

这是不正确的!!仅当您覆盖它以返回此类值时, toString() 才会返回 Mobile phone number。否则它将返回MOBILE_PHONE_NUMBER @spayny,很明显你必须覆盖toString()。问题是 name() 不能被覆盖,因为它是最终的。 @AlexR 您可能需要将上述评论合并到答案中以使其 100% 正确 我同意 @artfullyContrived 的观点,因为在我阅读您的 cmets 之前,这对我来说并不明显。【参考方案4】:

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

【讨论】:

【参考方案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

【讨论】:

是的,你是对的......一个很好的例子是番石榴的枚举谓词【参考方案6】:

虽然大多数人盲目地遵循 javadoc 的建议,但在某些非常特殊的情况下,您实际上希望避免使用 toString()。例如,我在 Java 代码中使用枚举,但它们需要序列化到数据库,然后再返回。如果我使用 toString() ,那么从技术上讲,正如其他人指出的那样,我会受到覆盖行为的影响。

此外,还可以从数据库中反序列化,例如,这应该始终在 Java 中工作:

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

虽然不能保证:

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

顺便说一句,我发现 Javadoc 明确地说“大多数程序员应该”是很奇怪的。我在枚举的 toString 中发现很少用例,如果人们将其用作“友好名称”,这显然是一个糟糕的用例,因为他们应该使用与 i18n 更兼容的东西,在大多数情况下,使用 name() 方法。

【讨论】:

感谢您的回答。我问这个问题已经快 5 年了,我还没有使用 toString() 方法进行枚举! 99% 的情况下,我完全按照您的描述使用枚举:将枚举名称序列化和反序列化到/从数据库。 如果您通过 Web 服务与现有系统(例如大型机)集成,或者只是为现有数据库创建新的前端,您将经常使用带有连字符的枚举值。【参考方案7】:

您也可以使用类似下面的代码。我使用 lombok 来避免为 getter 和构造函数编写一些样板代码。

@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;
    

【讨论】:

以上是关于Java 枚举 - 为啥使用 toString 而不是 name的主要内容,如果未能解决你的问题,请参考以下文章

java如何用方法toString()将枚举类型转换为字符串类型

为啥我们在枚举中写 Integer 而不是 int? [复制]

java enum类型

枚举ENUM的tostring() valueof()和values()用法

Java:为啥 \t 转义字符与 toString() 不一致? [复制]

三歪问我为啥用枚举,枚举有哪些用法?