2009-07-03 5 views
113

문자열 값 (또는 다른 값)에서 열거 형을 조회하고 싶습니다. 다음 코드를 시도했지만 초기화 프로그램에서 정적을 허용하지 않습니다. 간단한 방법이 있습니까?문자열 값에서 Java enum을 어떻게 조회 할 수 있습니까?

public enum Verbosity { 

    BRIEF, NORMAL, FULL; 

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>(); 

    private Verbosity() { 
     stringMap.put(this.toString(), this); 
    } 

    public static Verbosity getVerbosity(String key) { 
     return stringMap.get(key); 
    } 
}; 
+0

IIRC. 정적 초기화가 위에서 아래로 이루어지기 때문에 (즉, 상단의 열거 형 상수가'stringMap' 초기화까지 내려 가기 때문에) NPE를 제공합니다. 일반적인 해결책은 중첩 된 클래스를 사용하는 것입니다. –

+0

빠른 응답을 주신 모든 분들께 감사드립니다. (FWIW 나는 Sun Javadocs가이 문제에 매우 유용하다는 것을 발견하지 못했다.) –

+0

정말 라이브러리 문제보다 언어 문제입니다. 그러나, API 문서는 JLS (아마도 언어 디자이너가 아닐지라도)보다 더 많이 읽혀 지므로, 이와 같은 것들이 java.lang 문서에서 더 두드러 질 것입니다. –

답변

171

가 자동으로 각 열거 생성 된 valueOf 방법을 사용합니다.

public static Verbosity findByAbbr(String abbr){ 
    for(Verbosity v : values()){ 
     if(v.abbr().equals(abbr)){ 
      return v; 
     } 
    } 
    return null; 
} 

만 당신의 프로파일은 당신이 알 수 있다면 구현을지도하기 위해 나중에 이동 : 임의의 값의 경우

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF 

로 시작.

나는 그것이 모든 값을 반복하고 있다는 것을 알고있다. 그러나 단지 3 개의 열거 형 값으로는 다른 어떤 노력보다 가치가 없다. 사실 많은 가치가 없다면지도가별로 좋지 않을 것이다.

+0

감사합니다. 값이 여전히 분명한 경우에는 대소 문자 변환을 사용할 수 있습니다. –

+0

"v.abbr()"대신 "v.abbr.equals"를 사용할 수 있도록 클래스 멤버 'abbr'에 직접 지시 할 수 있습니다. ". – zatziky

+3

또한, 임의 값 (두 번째 경우)에 대해 null (* 즉.)을 반환하는 대신 'IllegalArgumentException'을 던집니다.* 일치하는 것이 발견되지 않을 때) - 이것은 JDK'Enum.valueOf (..)'가 어쨌든 그것을하는 방법입니다. –

6

그리고 valueOf()을 사용할 수 없습니까?

편집 : Btw, 열거 형에서 정적 {}을 (를) 사용하는 것을 방해하는 요소는 없습니다.

+0

또는 열거 형 클래스의 합성 'valueOf'입니다. 그래서 열거 형 클래스'Class'를 지정할 필요가 없습니다. –

+0

물론 javadoc 항목이 없으므로 링크하기가 어려웠습니다. – Fredrik

+1

JLS에 연결할 수 있습니다. http://java.sun.com/docs/books/jls/third_edition/html/classes.html#302265 – newacct

103

끝났습니다. 임의의 값의 경우, 다음과 같이 뭔가를 시도 :

public enum Day { 

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"), 
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ; 

    private final String abbreviation; 

    // Reverse-lookup map for getting a day from an abbreviation 
    private static final Map<String, Day> lookup = new HashMap<String, Day>(); 

    static { 
     for (Day d : Day.values()) { 
      lookup.put(d.getAbbreviation(), d); 
     } 
    } 

    private Day(String abbreviation) { 
     this.abbreviation = abbreviation; 
    } 

    public String getAbbreviation() { 
     return abbreviation; 
    } 

    public static Day get(String abbreviation) { 
     return lookup.get(abbreviation); 
    } 
} 
+2

"EnumSet.allOf (Day.class)"대신 "Day.values ​​()"를 사용할 수 있습니다 ... –

+0

아, 그렇게 할 수 있습니다. 감사. – Lyle

+2

클래스 로더 문제로 인해이 방법은 신뢰할 수 없습니다. 추천하지 않으며 정기적으로 조회가 이루어지기 때문에 룩업 맵이 액세스되기 전에 준비되지 않았기 때문에 실패합니다. enum이나 다른 클래스에 "lookup"을 넣어서 먼저로드해야합니다. –

12

@ Lyle의 답변은 다소 위험합니다. 특히 열거 형을 정적 인 내부 클래스로 만들면 작동하지 않습니다. 대신 나는 enums 전에 BootstrapSingleton 맵을로드하는 이와 같은 것을 사용했다.

편집이 더 이상 현대의 JVM에 문제 (JVM 1.6 이상을) 안하지만 난 거기에 JRebel 문제가 여전히하지만 난 그것을을 다시 테스트 할 기회가 없었어요 생각하십니까.

나를 먼저로드 :

public final class BootstrapSingleton { 

     // Reverse-lookup map for getting a day from an abbreviation 
     public static final Map<String, Day> lookup = new HashMap<String, Day>(); 
    } 

을 지금 열거 생성자에서로드 :에 (당신이 내부 열거가있는 경우

public enum Day { 
     MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"), 
     THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ; 

     private final String abbreviation; 

     private Day(String abbreviation) { 
      this.abbreviation = abbreviation; 
      BootstrapSingleton.lookup.put(abbreviation, this); 
     } 

     public String getAbbreviation() { 
      return abbreviation; 
     } 

     public static Day get(String abbreviation) { 
      return lookup.get(abbreviation); 
     } 
    } 

방금 ​​열거 정의 위의지도를 정의하고 있습니다 이론)은 전에로드되어야합니다.

+2

"위험한"클래스를 정의하고 내부 클래스 구현이 중요한 이유를 정의해야합니다. 위에서 정의 된 정적 정의 문제에 더 가깝지만이 질문에 대한 링크가있는 다른 관련 답변에 작업 코드가 있습니다.이 질문에 대한 사용자 정의 값에서부터 "get"메서드로 Enum 인스턴스 자체에 대한 정적 Map을 사용합니다. 열거 형. http://stackoverflow.com/questions/604424/lookup-enum-by-string-value –

+1

@DarrellTeague 원래 문제는 이전 JVM (1.6 이전) 또는 다른 JVM (IBM) 또는 핫 코드 스왑 기 (JRebel) 때문이었습니다. 현대 JVM에서는이 문제가 발생하지 않아야하므로 제 대답을 삭제할 수 있습니다. –

+0

커스텀 컴파일러 구현과 런타임 최적화로 인해 실제로 가능하다는 것을 알아두면 ... 순서가 재 배열되어 오류가 발생할 수 있습니다. 그러나 이것은 사양을 위반하는 것으로 보입니다. –

1

아마도이 부분을 살펴보십시오. 나를 위해 일하고. 'RED'를 '/ red_color'로 찾아 보는 것입니다. enum이 많은 경우 static map을 선언하고 enum을 한 번만로드하면 성능상의 이점이 있습니다.

public class Mapper { 

public enum Maps { 

    COLOR_RED("/red_color", "RED"); 

    private final String code; 
    private final String description; 
    private static Map<String, String> mMap; 

    private Maps(String code, String description) { 
     this.code = code; 
     this.description = description; 
    } 

    public String getCode() { 
     return name(); 
    } 

    public String getDescription() { 
     return description; 
    } 

    public String getName() { 
     return name(); 
    } 

    public static String getColorName(String uri) { 
     if (mMap == null) { 
      initializeMapping(); 
     } 
     if (mMap.containsKey(uri)) { 
      return mMap.get(uri); 
     } 
     return null; 
    } 

    private static void initializeMapping() { 
     mMap = new HashMap<String, String>(); 
     for (Maps s : Maps.values()) { 
      mMap.put(s.code, s.description); 
     } 
    } 
} 
} 

귀하의 의견을 입력하십시오.

-2

당신은 위의 가레스 데이비스 & 브래드 메이스에 의해 제안으로 Enum::valueOf() 기능을 사용하지만 사용 된 문자열이 열거에없는 경우 던져 질 것이다 IllegalArgumentException을 처리 확인 할 수 있습니다.

4

Java 언어 사양 7에는 a example!자체 참조가있는지도 초기화에 대한 질문을 반영합니다.

+1

조슈아 블로흐 (Joshua Bloch)의 효과적인 자바 (Effective Java)에서 사용되는 레퍼런스를 지적하는 것이 우수 ... –

5

자바 8이 방법으로 달성 할 수

public enum Verbosity 
{ 
    BRIEF, NORMAL, FULL, ACTION_NOT_VALID; 
    private int value; 

    public int getValue() 
    { 
    return this.value; 
    } 

    public static final Verbosity getVerbosityByValue(int value) 
    { 
    for(Verbosity verbosity : Verbosity.values()) 
    { 
     if(verbosity.getValue() == value) 
      return verbosity ; 
    } 

    return ACTION_NOT_VALID; 
    } 

    @Override 
    public String toString() 
    { 
     return ((Integer)this.getValue()).toString(); 
    } 
}; 

가 :

이 경우
public static Verbosity findByAbbr(final String abbr){ 
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null); 
} 
1

가 도움을 다른 사람, 여기에 나열되지 않은 선호하는 옵션은를 사용합니다. 10 : 당신이 null를 사용할 수있는 기본으로

public enum Vebosity { 
    BRIEF("BRIEF"), 
    NORMAL("NORMAL"), 
    FULL("FULL"); 

    private String value; 
    private Verbosity(final String value) { 
     this.value = value; 
    } 

    public String getValue() { 
     return this.value; 
    } 

    private static ImmutableMap<String, Verbosity> reverseLookup = 
      Maps.uniqueIndex(Arrays.asList(Verbosity.values)), Verbosity::getValue); 

    public static Verbosity fromString(final String id) { 
     return reverseLookup.getOrDefault(id, NORMAL); 
    } 
} 

, 당신은 throw IllegalArgumentException 또는 fromString 당신이 선호하는 어떤 행동 Optional을 반환 할 수 있습니다.

관련 문제