【问题标题】:Override valueof() and toString() in Java enum在 Java 枚举中覆盖 valueof() 和 toString()
【发布时间】:2012-03-28 13:31:15
【问题描述】:

enum 中的值是需要在其中包含空格的单词,但枚举的值中不能包含空格,因此它们都是捆绑在一起的。我想覆盖 toString() 在我告诉它的地方添加这些空间。

当我在添加空格的同一字符串上使用 valueOf() 时,我还希望枚举提供正确的枚举。

例如:

public enum RandomEnum
{
     StartHere,
     StopHere
}

在值为StartHere 的RandomEnum 上调用toString(),返回字符串"Start Here"。对同一字符串 ("Start Here") 调用 valueof() 返回枚举值 StartHere。

我该怎么做?

标签: java enums overriding tostring value-of


【解决方案1】:

我认为你不会让 valueOf("Start Here") 工作。 但就空间而言...尝试以下...

static private enum RandomEnum {
    R("Start There"), 
    G("Start Here"); 
    String value;
    RandomEnum(String s) {
        value = s;
    }
}

System.out.println(RandomEnum.G.value);
System.out.println(RandomEnum.valueOf("G").value);

Start Here
Start Here

【讨论】:

    【解决方案2】:

    您可以试用此代码。由于您无法覆盖 valueOf 方法,因此您必须定义一个自定义方法(下面的示例代码中的 getEnum),它返回您需要的值并更改您的客户端以改为使用此方法。

    public enum RandomEnum {
    
        StartHere("Start Here"),
        StopHere("Stop Here");
    
        private String value;
    
        RandomEnum(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
    
        @Override
        public String toString() {
            return this.getValue();
        }
    
        public static RandomEnum getEnum(String value) {
            for(RandomEnum v : values())
                if(v.getValue().equalsIgnoreCase(value)) return v;
            throw new IllegalArgumentException();
        }
    }
    

    【讨论】:

    • 如果您执行以下操作,getEnum 可能会缩短: v.getValue().equals(value);可以省略空检查。
    • 您也可以懒惰地构建地图并重复使用它,但在构建时要注意线程问题。
    • 在无法将 valueOf() 方法的调用更改为 getValue() 的情况下不起作用,例如当您通过 Hibernate 注释定义某些字段以映射到枚举值时
    • 直到枚举在 Java 中被修复,@Bat 有一个更合适和 OO 友好的解决方案。 name() 不应该是最终的。
    • 代替你可以使用 valueOf(RandomEnum.class, value);
    【解决方案3】:

    以下是 valueOf() 的一个很好的通用替代方法

    public static RandomEnum getEnum(String value) {
      for (RandomEnum re : RandomEnum.values()) {
        if (re.description.compareTo(value) == 0) {
          return re;
        }
      }
      throw new IllegalArgumentException("Invalid RandomEnum value: " + value);
    }
    

    【讨论】:

    • 在字符串中使用compareTo() 是否比equals() 更优雅?
    • 优雅不是问题——我同意.equals() 可以正常工作。如果您愿意,可以使用==。 (虽然有充分的理由不应该用类替换枚举。)
    • @exception 永远不要使用 == 进行字符串比较,因为它会进行对象相等性检查,而您需要的是内容相等性检查并为此使用 String 类的 equals() 方法。
    • 不适用于 Java 6+
    【解决方案4】:

    您可以在枚举中使用静态映射,将字符串映射到枚举常量。在“getEnum”静态方法中使用它。这样就无需在每次您想从其 String 值中获取枚举时遍历枚举。

    public enum RandomEnum {
    
        StartHere("Start Here"),
        StopHere("Stop Here");
    
        private final String strVal;
        private RandomEnum(String strVal) {
            this.strVal = strVal;
        }
    
        public static RandomEnum getEnum(String strVal) {
            if(!strValMap.containsKey(strVal)) {
                throw new IllegalArgumentException("Unknown String Value: " + strVal);
            }
            return strValMap.get(strVal);
        }
    
        private static final Map<String, RandomEnum> strValMap;
        static {
            final Map<String, RandomEnum> tmpMap = Maps.newHashMap();
            for(final RandomEnum en : RandomEnum.values()) {
                tmpMap.put(en.strVal, en);
            }
            strValMap = ImmutableMap.copyOf(tmpMap);
        }
    
        @Override
        public String toString() {
            return strVal;
        }
    }
    

    只要确保映射的静态初始化发生在枚举常量声明的下方即可。

    顺便说一句 - 'ImmutableMap' 类型来自 Google guava API,在这种情况下我绝对推荐它。


    编辑 - 根据 cmets:

    1. 此解决方案假定每个分配的字符串值都是唯一且非空的。鉴于枚举的创建者可以控制这一点,并且字符串对应于唯一且非空的枚举值,这似乎是一个安全的限制。
    2. 我添加了问题中要求的“toSTRing()”方法

    【讨论】:

    • 我在这个答案上收到了几票反对票 - 有人愿意详细说明为什么这个答案不好吗?我只是好奇人们在想什么。
    • 对于具有 多个 具有相同“值”的枚举常量(例如 RestartHere("replay"), ReplayHere("replay"))或根本没有“值”(例如 PauseHere, WaitHere)的场景,这将失败(即枚举具有默认值)构造函数类似于private RandomEnum() { this.strVal = null; }
    • 您应该使用 ImmutableMap.Builder 而不是创建 MutableMap 来构建 + ImmutableMap::copyOf。
    • 这看起来很有趣,但是如果枚举只有几个不同的值,那么迭代它们不会花费很长时间。如果枚举有很多值,这可能才值得。
    【解决方案5】:

    试试这个,但我不确定它是否会在任何地方都有效:)

    public enum MyEnum {
        A("Start There"),
        B("Start Here");
    
        MyEnum(String name) {
            try {
                Field fieldName = getClass().getSuperclass().getDeclaredField("name");
                fieldName.setAccessible(true);
                fieldName.set(this, name);
                fieldName.setAccessible(false);
            } catch (Exception e) {}
        }
    }
    

    【讨论】:

    • 为什么我是唯一一个让那种东西看起来很邪恶的人?我的意思是它看起来很酷,但是没有上下文的人阅读该代码很可能会像“WTF?”。
    • @NicolasGuillaume 这种代码如果被实施,将迫切需要注释解释它为什么存在以及程序员试图解决什么问题。 :-)
    • 不要捕获通用异常,因为这可能是 OutOfMemory 或 anything
    • 这是一个糟糕的主意。其中最重要的一点是无提示或没有恢复的静默错误的可能性。现在,“名称”值和代码中的符号值(例如 A、B)也不同了。 +2 聪明。 -200 非常聪明。我绝不会批准这是一次代码审查。
    • @pedorro 这似乎并没有你想象的那么糟糕。任何理解 Java 委员会所做的一切都不应该被视为福音的人都会在代码审查中通过这一点。由于 name() 不能被覆盖,因此必须重写然后维护所有 Enum 实用程序方法会更加糟糕。与其捕获异常并吞下它,不如抛出一个 RuntimeException,这样就可以了。
    【解决方案6】:

    Java 8 实现怎么样? (null 可以替换为你的默认枚举)

    public static RandomEnum getEnum(String value) {
        return Arrays.stream(RandomEnum.values()).filter(m -> m.value.equals(value)).findAny().orElse(null);
    }
    

    或者你可以使用:

    ...findAny().orElseThrow(NotFoundException::new);
    

    【讨论】:

      【解决方案7】:

      你仍然可以选择在你的枚举中实现这个:

      public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多