2009-08-11 19 views
17

enum値をHTML <select>要素で表示するのに適した文字列のリストに変換するヘルパーメソッドがいくつかあります。私は、これらを単一の多型方法にリファクタリングすることが可能かどうか疑問に思っていました。Javaのenum値を文字列のリストに多面的に変換する

これは私の既存の方法の一つの例である:

/** 
* Gets the list of available colours. 
* 
* @return the list of available colours 
*/ 
public static List<String> getColours() { 
    List<String> colours = new ArrayList<String>(); 

    for (Colour colour : Colour.values()) { 
    colours.add(colour.getDisplayValue()); 
    } 

    return colours; 
} 

は、私はまだJavaのジェネリックにはかなり新しいので、私は、メソッドにジェネリック列挙型を渡すするかどうかはわかりませんし、ことがありますforループ内でも使用されます。

問題の列挙型にはすべてgetDisplayValueメソッドが含まれていますが、残念ながらそれらを定義する一般的な型は共有されていないため、これを導入する必要はありません反射的にアクセスされる...?

ご協力いただきありがとうございます。 Java用

答えて

17

は簡単です:

static <T extends Enum<T>> List<String> toStringList(Class<T> clz) { 
    try { 
     List<String> res = new LinkedList<String>(); 
     Method getDisplayValue = clz.getMethod("getDisplayValue"); 

     for (Object e : clz.getEnumConstants()) { 
      res.add((String) getDisplayValue.invoke(e)); 

     } 

     return res; 
    } catch (Exception ex) { 
     throw new RuntimeException(ex); 
    } 
} 

getDisplayValueメソッドなしのEnumを持つことができるので、これは完全に型保証されていません。どこかのユーティリティクラスの最初のヘルパーメソッドと静的内部クラス

:ここ

+0

getDisplayValueメソッドを使用しません。 –

+0

が変更されました。例外処理をご確認ください – dfa

+0

うまく動作します、ありがとう! –

-1

(申し訳ありませんが、これはC#です。私は質問は、Javaのためだったことがわかりませんでした。)

public string[] GetValues<T>() 
{ 
    return Enum.GetNames(typeof(T)); 
} 

、もちろん、すべてのenum型はまだそう、java.util.Enum継承あなたが書くことができます。java.util.Enumは実際の値()を実装していないので

public string[] getValues<T extends Enum<T>>() 
{ 
    // use reflection on T.class 
} 

を、私は反射が行くための唯一の方法だと思います。

+0

あなたのJavaコードはコンパイルされません。 FYI Enum.GetNames(typeof(T))の代わりにJavaでaClass.getEnumValues()を使用してください – dfa

3

ここでは2つのことがあります。あなたが本当にタイプという内部的に気にしている場合

public interface DisplayableSelection { 
    public String getDisplayValue(); 
} 

private static List<String> getStrings (Collection<DisplayableSelection> things) { 
    List<String> retval = new ArrayList<String>(); 
    for (DisplayableSelection thing : things) { 
    retval.add(thing.getDisplayValue()); 
    } 
} 

private static List<String> getColours() { 
    return getStrings(Colour.values()); 
} 

:最初の(単純、したがって、より良い)方法は、ちょうどあなたのgetStringsを()メソッドは、いくつかのインターフェイスのリストを取り、あなたの列挙型は、そのインターフェイスを実装作る持っているだろうEnum型であれば、すべての列挙型が組み込み型Enumを自動的にサブクラス化するという事実を使うこともできます。だから、あなたのたとえば(免責事項:私はこれがコンパイルを考えるが、実際にそれを試していない):あなたは、具体的列挙を必要とする何かをしたい場合は

public interface DisplayableEnum { 
    public String getDisplayValue(); 
} 

private static <T extends Enum<T> & DisplayableEnum > List<String> getDisplayValues(Class<T> pClass) { 
    List<String> retval = new ArrayList<String>(); 
    for (DisplayableSelection thing : pClass.getEnumConstants()) { 
    retval.add(thing.getDisplayValue()); 
    } 
} 

private static List<String> getColours() { 
    return getStrings(Colour.class); 
} 

この第二の形式は便利です(たとえば、何らかの理由でEnumMapまたはEnumSetを使用します)。それ以外の場合は、最初のメソッドを使用します(このメソッドを使用すると、列挙型以外の列挙型、または列挙型のサブセットのみを使用できます)。 、私は 問題の列挙型はすべて、その getDisplayValue方法を持っていることを知っているが、

+0

ありがとうございましたが、最初のオプションは私には利用できないとすでに説明しました。 –

+0

+1 2番目のオプションは、getDisplayValue()が実装されていることを確認して、受け入れられた応答タイプを安全にします。反射を除去する。 –

+0

第2のオプションとしても+1、リフレクションなし、@Sbodd you rock ;-) Scalaのenum、errrの実装のためにJavaで回避しようとしていた問題をエレガントに解決しました。 – virtualeyes

0

注は、残念ながら、彼らはそれを定義する 一般的なタイプを共有しない(と私は 1を導入することはできません)だから私は には に反射的にアクセスしなければならないと思います...?

あなたは正しく推測しています。代わりに、表示値を返すことによって列挙型にすべてtoString()を実装させることもできますが、インタフェースを実装できない場合は、それもできないと思います。

+0

残念ながら、toStringの出力は、変更できないデータベースの値と一致する必要があるため、どちらもできません。 –

10

あなたは、いくつかのユーティリティクラスでこのメソッドを固執することができます:

public static <T extends Enum<T>> List<String> getDisplayValues(Class<T> enumClass) { 
    try { 
     T[] items = enumClass.getEnumConstants(); 
     Method accessor = enumClass.getMethod("getDisplayValue"); 

     ArrayList<String> names = new ArrayList<String>(items.length); 
     for (T item : items) 
      names.add(accessor.invoke(item).toString())); 

     return names; 
    } catch (NoSuchMethodException ex) { 
     // Didn't actually implement getDisplayValue(). 
    } catch (InvocationTargetException ex) { 
     // getDisplayValue() threw an exception. 
    } 
} 

出典:Examining Enums

+0

getDisplayValueメソッドを使用しません。 –

+0

ええ、私が投稿した後にそれを実現しました。それは今です。 –

+0

Displayableはどこから来たのですか? –

2

を私はtoString(そしておそらくクラス名)値にマップするバンドルファイルをjava.util.ResourceBundleを使用したいですあなたの列挙型のコードは、その後のようなものになりますので:Class#getEnumConstants()を使用して

bundle.getString(enum.getClass().getName() + enum.toString()); 
1

は、私はそれについて行くことをお勧めする方法です

@SuppressWarnings("unchecked") 
    public static <T> T generateProxy(Object realObject, Class<?>... interfaces) { 
     return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject)); 
    } 


    private static class SimpleInvocationHandler implements InvocationHandler { 
     private Object invokee; 

     public SimpleInvocationHandler(Object invokee) { 
      this.invokee = invokee; 
     } 

     public Object invoke(Object proxy, Method method, Object[] args) 
       throws Throwable { 
      method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes()); 
      if (!method.isAccessible()) { 
       method.setAccessible(true); 
      } 
      try { 
       return method.invoke(invokee, args); 
      } catch (InvocationTargetException e) { 
       throw e.getTargetException(); 
      } 
     } 
    } 

そしてあなたの列挙型と一緒にそれを置く:

interface DisplayableEnum { 
     String getDisplayValue(); 
    } 

private static List<String> getFromEnum(Class<? extends Enum<?>> displayableEnum) { 
    List<String> ret = new ArrayList<String>(); 
    for (Enum e : displayableEnum.getEnumConstants()) { 
     DisplayableEnum de = generateProxy(e, DisplayableEnum.class); 
     ret.add(de.getDisplayValue()); 
    } 
    return ret; 
} 

パフォーマンスが非常に多くのプロキシオブジェクトを生成する問題である場合、私はDisplayableEnumを実装する変更可能なクラスを作成する道を進んでいます各enum定数(フライウェイトパターンの種類)でハンギングし、その実際のオブジェクトに対してより柔軟な呼び出しハンドラを用意し、ループを通過するたびに正しいものを呼び出します。

3

このアプローチは、反射を避ける:

public static interface Displayer<T> { 
    String displayName(T t); 
    } 

    public static <T> List<String> getDisplayNames(Iterable<? extends T> stuff, 
     Displayer<T> displayer) { 
    List<String> list = new ArrayList<String>(); 
    for (T t : stuff) { 
     list.add(displayer.displayName(t)); 
    } 
    return list; 
    } 

...しかし、あなたが表示したいすべてのための別のタイプ必要ない:この場合

enum Foo { 
    BAR("BAR"), BAZ("BAZ"); 
    private final String displayName; 

    private Foo(String displayName) { 
     this.displayName = displayName; 
    } 

    public String getDisplayName() { 
     return displayName; 
    } 
    } 

    public static void main(String[] args) { 
    Displayer<Foo> fooDisplayer = new Displayer<Foo>() { 
     public String displayName(Foo foo) { 
     return foo.getDisplayName(); 
     } 
    }; 

    System.out.println(getDisplayNames(Arrays.asList(Foo.BAR, Foo.BAZ), 
     fooDisplayer)); 
    } 

を、匿名型を使用しているが、それは無国籍のシングルトンやsomesuchかもしれません。

0

私は、最初の応答に、このよう方法で編集し、それが問題なく動作するとせずに任意のインターフェイス

public static <T extends Enum<T>> List<String> getDisplayValues(
     Class<T> enumClass) { 
    try { 
     T[] items = enumClass.getEnumConstants(); 
     Method accessor = enumClass.getMethod("toString"); 

     ArrayList<String> names = new ArrayList<String>(items.length); 
     for (T item : items) 
      names.add(accessor.invoke(item).toString()); 

     return names; 
    } catch (NoSuchMethodException ex) { 
     // Didn't actually implement getDisplayValue(). 
     Log.e(TAG, "getDisplayValues [" + ex+"]"); 
    } catch (InvocationTargetException ex) { 
     // getDisplayValue() threw an exception. 
     Log.e(TAG, "getDisplayValues [" + ex+"]"); 
    } catch (IllegalAccessException ex) { 
     // getDisplayValue() threw an exception. 
     Log.e(TAG, "getDisplayValues [" + ex+"]"); 
    } 
    return null; 
} 
-4

は、そのハードそのない...みんなに来て実装します。私は文字列の比較を使用して..しかし、あなたが望む場合は、オブジェクトの種類を比較することができます。

public static <T extends Enum<T>> Map<T, String> Initialize_Map(Class<T> enumClass) { 
    Map<T, String> map = new HashMap<T, String>(); 
    for (T val : enumClass.getEnumConstants()) { 
    map.put(val, val.toString() + (val.toString().equals("ENUM_ELMT") ? " (appended)" : "")); 
    } 
    return map;  
} 
関連する問題