2009-08-11 126 views
17

我有一些幫助器方法可將枚舉值轉換爲適合由HTML <select>元素顯示的字符串列表。我想知道是否有可能將這些重構爲單一的多態方法。將Java枚舉值多態轉換爲字符串列表

這是我現有的方法中的一個例子:

/** 
* 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方法,但不幸的是它們沒有共享一個定義它的通用類型(我不能引入它),所以我想這將不得不反射式地訪問...?

在此先感謝您的幫助。

回答

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方法。

+0

不使用getDisplayValue方法。 – 2009-08-11 16:08:04

+0

修改,請查看異常處理 – dfa 2009-08-11 16:14:55

+0

很好地工作,謝謝! – 2009-08-11 16:16:55

-1

(抱歉,這是C#。我沒有看到這個問題是針對Java)

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

對於Java,當然,所有enum類型仍然java.util.Enum繼承,所以你可以這樣寫:

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

由於java.util.Enum並不實際執行值(),我認爲這反映是去的唯一途徑。

+0

你的Java代碼甚至沒有編譯;僅供參考在Java中使用aClass.getEnumValues()代替Enum.GetNames(typeof(T)) – dfa 2009-08-11 16:21:13

3

你可以在這裏做兩件事。第一個(簡單,因此更好)的方式也只是有你getStrings()方法需要一些界面的列表,讓你的枚舉實現該接口:

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

如果你真的在內部關心的類型是一個枚舉,你也可以使用這樣一個事實,即所有的枚舉類型都自動地將內建類型的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);否則,我會去第一個(因爲使用該方法,你也可以使用非枚舉類型,或者只是枚舉的一個子集)。

+0

謝謝,但我已經解釋過,第一個選項對我不可用。 – 2009-08-11 15:59:49

+0

對於第二個選項爲+1,因爲它通過確保它實現getDisplayValue()來使接受的答案類型安全。並消除反射。 – 2011-11-24 12:55:38

+0

對於第二個選項+1,沒有反思,@Sbodd你搖滾;-)優雅地解決了一個問題,我一直試圖解決Java的問題,因爲Scala枚舉,錯誤實現。 – virtualeyes 2012-01-18 18:41:37

0

注意,我知道,在 問題枚舉都會有 getDisplayValue方法,但 不幸的是他們不共享 常見的類型,定義它(和我 不能介紹一個)所以我想 將不得不 反射...?

你猜對了。另一種方法是讓枚舉全部通過返回顯示值來實現toString() - 但是如果你不能讓它們實現一個接口,那麼我想這也是不可能的。

+0

不幸的是我不能這樣做,因爲toString的輸出必須匹配我無法更改的數據庫中的值! – 2009-08-11 16:03:26

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方法。 – 2009-08-11 16:06:41

+0

是的,我發佈後意識到。它現在。 – 2009-08-11 16:09:22

+0

Displayable從哪裏來? – 2009-08-11 16:10:40

2

我會使用一個java.util.ResourceBundle與映射到toString(也許類名)值的包文件您枚舉所以你的代碼就變成這樣的:使用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,可以c每一個枚舉常量(一種flyweight模式)都有變化,並且有一個調用處理函數,它對於它的真實對象更加靈活,並且在每次循環中調用正確的對象。

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

在這種情況下,一個匿名類型被使用,但它可能是一個無狀態的單身人士或其他人。

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