我有一些辅助方法可以将枚举值转换为适合HTML <select>
元素显示的字符串列表。我想知道是否可以将这些重构为单一的多态方法。
这是我现有方法之一的示例:
/**
* 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
方法,但不幸的是它们没有共享定义它的常见类型(我不能介绍一个),所以我猜必须反思性地访问......?
提前感谢您的帮助。
答案 0 :(得分:17)
使用Class#getEnumConstants()很简单:
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
方法的枚举。
答案 1 :(得分: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.
}
}
答案 2 :(得分: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,您还可以使用所有枚举类型自动子类化内置类型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),则第二种形式可能很有用;否则,我会选择第一个(因为使用该方法,您还可以使用非枚举类型,或仅使用枚举的子集)。
答案 3 :(得分: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));
}
在这种情况下,使用匿名类型,但它可能是无状态单例或某些。
答案 4 :(得分:2)
我会使用java.util.ResourceBundle
一个捆绑文件,该文件会映射到您的枚举的toString
(也许是类名)值,这样您的代码就会变成:
bundle.getString(enum.getClass().getName() + enum.toString());
答案 5 :(得分: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的可变类的路径,该类可以随着每个枚举常量(一种flyweight模式)而改变,并在那里有一个调用处理程序对于它的真实对象更灵活,并在每次循环中调用正确的对象。
答案 6 :(得分:0)
请注意,我知道枚举 问题都会有 getDisplayValue方法,但是 不幸的是他们没有分享 定义它的常见类型(和我 不能介绍一个),所以我猜 必须访问 反射性...?
你猜对了。另一种方法是让enums通过返回显示值来实现toString()
- 但是如果你不能让它们实现一个接口,那么我想这也是不可能的。
答案 7 :(得分: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;
}
答案 8 :(得分:-1)
(抱歉,这是C#。我没有看到问题是针对Java的。)
public string[] GetValues<T>()
{
return Enum.GetNames(typeof(T));
}
对于Java,当然,所有枚举类型仍然继承自java.util.Enum,因此您可以编写:
public string[] getValues<T extends Enum<T>>()
{
// use reflection on T.class
}
由于java.util.Enum实际上没有实现values(),我认为反射是唯一的方法。
答案 9 :(得分:-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;
}