Java enumのvalueof()およびtoString()をオーバーライドする


122

myの値は、enumスペースを必要とする単語ですが、列挙型は値にスペースを含めることができないため、すべてまとめてあります。オーバーライドtoString()したい場所にこれらのスペースを追加してオーバーライドします。

またvalueOf()、スペースを追加したのと同じ文字列で使用するときに、列挙型が正しい列挙型を提供するようにします。

例えば:

public enum RandomEnum
{
     StartHere,
     StopHere
}

値がtoString()RandomEnumある呼び出しStartHereはstringを返します"Start Here"valueof()同じ文字列を呼び出すと、"Start Here"列挙値が返されますStartHere

これどうやってするの?


2
「Java」タグを追加して、より多くのコメント/回答を取得します。
Java42 2012年

回答:


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); nullチェックは省略できます。
rtcarlson 2014年

また、マップを遅延作成して再利用することもできますが、作成中のスレッドの問題に注意してください。
Maarten Bodewes 2014年

11
valueOf()メソッドの呼び出しをgetValue()に変更できないシナリオでは機能しません。たとえば、Hibernateアノテーションを介して特定のフィールドを定義して列挙値にマップする場合
mmierins

EnumがJavaで修正されるまで、@ Batには、より適切でオブジェクト指向に適したソリューションがあります。name()はfinalであってはなりません。
Andrew T Finnell、2018

代わりに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

9
これはOutOfMemoryをかかもしれないとして、一般的な例外をキャッチしないでください何も
crusy

2
これはひどい考えです。兆候や回復のないサイレントエラーが発生する可能性もその1つです。また、「名前」の値とコード内のシンボリック値(A、Bなど)が異なります。+2賢い。ひどく賢いのは-200。これをコードレビューとして承認する方法はありません。
ペドルロ

1
@pedorroこれはあなたが考えているほどひどくはないようです。Gospelがコードレビューでこれを通過するため、Java委員会が行うすべてのことを行うべきではないことを理解した人は誰でもいます。name()はオーバーライドできないため、すべてのEnumユーティリティメソッドを書き直して維持する必要があることは、無限に悪化しています。例外をキャッチしてそれを飲み込む代わりに、RuntimeExceptionをスローする必要があります。これで問題ありません。
Andrew T Finnell、2018

14

文字列を列挙型定数にマップする列挙型で静的マップを使用できます。'getEnum'静的メソッドで使用します。これにより、文字列値から取得するたびに列挙を反復処理する必要がなくなります。

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からのものであり、このような場合は絶対にお勧めします。


編集-コメントごと:

  1. このソリューションは、割り当てられた各文字列値が一意であり、nullでないことを前提としています。列挙型の作成者がこれを制御でき、文字列が一意のnull以外の列挙値に対応している場合、これは安全な制限のようです。
  2. 質問で尋ねられたように「toSTring()」メソッドを追加しました

1
私はこの回答についていくつかの反対票を受け取りました-なぜこの回答が悪いのかを詳しく説明したい人はいますか?私は人々が何を考えているのか興味があるだけです。
pedorro

これは、シナリオが持つために失敗する複数の同じような「の値」でenum定数をRestartHere("replay"), ReplayHere("replay")好きかまったく「価値」PauseHere, WaitHere(つまり、列挙型のようなデフォルトコンストラクタの何かがあるprivate RandomEnum() { this.strVal = null; }
sactiw

1
MutableMapを作成する代わりに、ImmutableMap.Builderを使用して+ ImmutableMap :: copyOfを作成する必要があります。
ブライアンコレル

これは興味深いように見えますが、列挙型にいくつかの異なる値しかない場合は、それらを反復するのに長い時間はかかりません。enumに多くの値がある場合にのみ、おそらく価値があります。
Ben Thurley、2018

13

Java 8実装はどうですか?(nullはデフォルトのEnumで置き換えることができます)

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( "ここから開始")が機能することはないと思います。しかし、スペースに関しては...以下を試してください...

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クラスのequals()メソッドを使用しながらオブジェクトの等価性チェックを行うためです。
sactiw

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.