Java 열거 형에서 valueof () 및 toString () 재정의


122

내 값 enum은 공백이 있어야하는 단어이지만 열거 형은 값에 공백을 포함 할 수 없으므로 모두 묶여 있습니다. 내가 toString()말하는 곳에 이러한 공간을 추가 하도록 재정의하고 싶습니다 .

또한 valueOf()공백을 추가 한 동일한 문자열에서 사용할 때 열거 형이 올바른 열거 형을 제공하기를 원합니다 .

예를 들면 :

public enum RandomEnum
{
     StartHere,
     StopHere
}

전화 toString()RandomEnum그 값이 StartHere반환 문자열 "Start Here". valueof()동일한 문자열 ( "Start Here") 을 호출 하면 열거 형 값이 반환 StartHere됩니다.

어떻게 할 수 있습니까?


2
더 많은 의견 / 답변을 얻으려면 "Java"태그를 추가하십시오.
Java42

답변:


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

맵을 느리게 빌드하고 재사용 할 수도 있지만 빌드하는 동안 스레딩 문제에주의하십시오.
마틴 Bodewes

11
예를 들어 열거 형 값에 매핑하기 위해 Hibernate 주석을 통해 특정 필드를 정의 할 때 valueOf () 메서드 호출을 getValue ()로 변경할 수없는 시나리오에서는 작동하지 않습니다.
mmierins

Enums가 Java로 수정 될 때까지 @Bat에는 더 적절하고 OO 친화적 인 솔루션이 있습니다. name ()은 최종적이어서는 안됩니다.
Andrew T Finnell

대신 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
이것은 끔찍한 생각입니다. 그 중에서도 표시 또는 복구없이 자동 오류가 발생할 가능성이 있습니다. 또한 이제 "이름"값과 코드의 기호 값 (예 : A, B)이 다릅니다. 영리함을 위해 +2. -200은 매우 영리합니다. 코드 검토를 승인 할 방법이 없습니다.
pedorro

1
@pedorro 이것은 당신이 그것을 만드는 것만 큼 끔찍한 것 같지 않습니다. 자바위원회가하는 모든 일을 복음으로 받아 들여서는 안된다는 것을 이해 한 사람은 코드 리뷰에서 이것을 통과 할 것입니다. name ()을 재정의 할 수 없기 때문에 모든 Enum 유틸리티 메서드를 다시 작성하고 유지 관리해야하는 것은 극히 더 나쁩니다. 예외를 잡아서 삼키는 대신 RuntimeException을 던져야합니다. 그러면 괜찮습니다.
Andrew T Finnell

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

맵의 정적 초기화가 열거 형 상수 선언 아래에서 발생하는지 확인하십시오.

BTW- 'ImmutableMap'유형은 Google 구아바 API에서 가져온 것이며, 이런 경우에는 확실히 추천합니다.


편집-댓글에 따라 :

  1. 이 솔루션은 할당 된 각 문자열 값이 고유하고 null이 아니라고 가정합니다. 열거 형의 작성자가 이것을 제어 할 수 있고 문자열이 고유하고 null이 아닌 열거 형 값에 해당한다는 점을 감안할 때 이것은 안전한 제한처럼 보입니다.
  2. 질문에서 요청한대로 'toSTring ()'메서드를 추가했습니다.

1
이 답변에 대해 몇 개의 반대표를 받았습니다.이 답변이 왜 나쁜지 확장하려는 사람이 있습니까? 사람들이 무슨 생각을하는지 궁금합니다.
pedorro

이것은 같은 '값'과 같은 '값'과 같은 여러 enum 상수를 가지고 RestartHere("replay"), ReplayHere("replay")있거나 전혀 '값'이없는 시나리오에서는 실패 할 것입니다 PauseHere, WaitHere(즉, enum에는 다음과 같은 기본 생성자가 있습니다private RandomEnum() { this.strVal = null; }
sactiw

1
+ ImmutableMap :: copyOf를 빌드하기 위해 MutableMap을 만드는 대신 ImmutableMap.Builder를 사용해야합니다.
Bryan Correll

이것은 흥미로워 보이지만 열거 형에 몇 가지 다른 값만 있으면 반복하는 데 오래 걸리지 않을 것입니다. 열거 형에 많은 값이있는 경우에만 그만한 가치가 있습니다.
Ben Thurley

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 ( "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 클래스의 equals () 메소드를 사용하는 것입니다.
sactiw

2

열거 형에서 구현할 수있는 옵션이 여전히 있습니다.

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name){...}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.