在Java枚举中重写valueof()和toString()


122

enum中的值是需要在其中包含空格的单词,但是枚举的值中不能包含空格,因此它们都被堆砌了。我想覆盖toString()添加这些空格,我告诉它。

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

例如:

public enum RandomEnum
{
     StartHere,
     StopHere
}

电话toString()上的RandomEnum,其值是StartHere返回字符串"Start Here"。调用valueof()相同的字符串("Start Here")将返回枚举值StartHere

我怎样才能做到这一点?


2
添加“ Java”标签以获得更多评论/答案。
Java42 2012年

Answers:


197

您可以尝试此代码。由于无法覆盖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();
    }
}

4
如果执行以下操作,则可以缩短getEnum:v.getValue()。equals(value); 空检查可以省略。
rtcarlson

您也可以懒惰地构建地图并重用它,但是在构建它时要注意线程问题。
Maarten Bodewes 2014年

11
在无法将valueOf()方法的调用更改为getValue()的情况下不起作用,例如当您通过Hibernate批注定义某些字段以映射到枚举值时
mmierins 2014年

在用Java修复Enums之前,@ Bat具有更合适且面向对象的解决方案。name()不应为final。
Andrew T Finnell '18

而不是您可以使用valueOf(RandomEnum.class,value);
Wojtek

22

试试这个,但我不确定在所有地方都可以使用:)

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) {}
    }
}

18
为什么我是唯一那种看起来很邪恶的人?我的意思是,它看上去确实很酷,但是没有上下文的人只要看懂该代码,就很可能会像“ WTF?”。
Nicolas Guillaume

11
@NicolasGuillaume这是一种代码,如果实现了,则急需注释以说明其原因以及程序员试图解决的问题。:-)
Ti Strga '16

9
不要捕获一般的Exception,因为它可能是OutOfMemory或其他任何东西
crusy

2
这是一个可怕的想法。其中最重要的是无提示或无提示的无提示错误的可能性。现在,代码中的“名称”值和符号值(例如A,B)也不同。+2聪明。-200非常聪明。我绝对不会批准此代码审查。
pedorro '18

1
@pedorro这似乎并不像您确定的那样可怕。任何理解Java委员会所做的一切都不应该被视为福音的人会在代码审查中通过。由于无法覆盖name(),因此必须重新编写然后维护所有Enum实用程序方法,这将使情况更加恶化。与其捕获并吞下该异常,不如抛出RuntimeException,然后就可以了。
安德鲁·芬内尔

14

您可以在枚举中使用将字符串映射到枚举常量的静态Map。在“ 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番石榴API,在这种情况下,我绝对推荐使用。


编辑-根据评论:

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

1
我收到了一些关于这个答案的不赞成票-有人在乎为什么这个答案不好吗?我很好奇人们在想什么。
pedorro 2015年

对于具有多个 “值”相同RestartHere("replay"), ReplayHere("replay")或根本没有“值”的枚举常量的场景,这将失败PauseHere, WaitHere(即枚举具有默认的构造函数,如private RandomEnum() { this.strVal = null; }
sactiw 2015年

1
您应该使用ImmutableMap.Builder而不是创建MutableMap来构建+ ImmutableMap :: copyOf。
布莱恩·科雷尔

这看起来很有趣,但是如果枚举只有几个不同的值,那么迭代它们就不会花很长时间。仅当枚举具有很多值时才值得。
本·瑟利

13

Java 8实现怎么样?(可以用默认的Enum代替null)

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

或者您可以使用:

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

2

我认为您不会让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()的一个很好的通用替代品

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()字符串更优雅吗?
Betlista 2014年

优雅不是问题-我同意那.equals()会很好。可以根据需要使用==。(尽管有充分的理由不应该使用一个类替换该枚举。)
例外

3
@exception永不使用==进行String比较,因为它将在您需要进行内容相等性检查时执行对象相等性检查,并为此使用String类的equals()方法。
sactiw

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.