多态地将 Java 枚举值转换为字符串列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1261233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Polymorphically convert Java enum values into a list of strings
提问by John Topley
I have a handful of helper methods that convert enum values into a list of strings suitable for display by an HTML <select>
element. I was wondering if it's possible to refactor these into a single polymorphic method.
我有一些辅助方法可以将枚举值转换为适合 HTML<select>
元素显示的字符串列表。我想知道是否有可能将这些重构为一个单一的多态方法。
This is an example of one of my existing methods:
这是我现有方法之一的示例:
/**
* 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;
}
I'm still pretty new to Java generics, so I'm not sure how to pass a generic enum to the method and have that used within the for loop as well.
我对 Java 泛型还是很陌生,所以我不确定如何将泛型枚举传递给方法并在 for 循环中使用它。
Note that I know that the enums in question will all have that getDisplayValue
method, but unfortunately they don't share a common type that defines it (and I can't introduce one), so I guess that will have to be accessed reflectively...?
请注意,我知道有问题的枚举都将具有该getDisplayValue
方法,但不幸的是,它们不共享定义它的通用类型(我不能介绍一个),因此我想必须以反射方式访问它。 .?
Thanks in advance for any help.
在此先感谢您的帮助。
采纳答案by dfa
using Class#getEnumConstants()is simple:
使用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);
}
}
this is not completely typesafe since you can have an Enum without a getDisplayValue
method.
这不是完全类型安全的,因为您可以在没有getDisplayValue
方法的情况下使用 Enum 。
回答by JSB????
(sorry, this is C#. I didn't see that the question was for Java.)
(抱歉,这是 C#。我没有看到问题是针对 Java 的。)
public string[] GetValues<T>()
{
return Enum.GetNames(typeof(T));
}
For Java, of course, all enum types still inherit from java.util.Enum, so you can write:
当然,对于Java,所有的枚举类型仍然继承自java.util.Enum,所以你可以这样写:
public string[] getValues<T extends Enum<T>>()
{
// use reflection on T.class
}
Since java.util.Enum doesn't actually implement values(), I think that reflection is the only way to go.
由于 java.util.Enum 实际上并没有实现 values(),我认为反射是唯一的出路。
回答by Sbodd
There are two things you can do here. The first (simpler, and therefore better) way would just be to have your getStrings() method take a list of some interface, and make your enums implement that interface:
您可以在这里做两件事。第一种(更简单,因此更好)的方法就是让您的 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());
}
If you really care internally that the type is an Enum, you can also use the fact that all enumerated types automatically subclass the built-in type Enum. So, for your example (disclaimer: I thinkthis compiles, but haven't actually tried it):
如果您真的在乎内部类型是 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);
}
This second form can be useful if you want to do something that specifically requires an enumeration (e.g. use an EnumMap or EnumSet for some reason); otherwise, I'd go with the first (since with that method, you can also use non-enumerated types, or just a subset of the enumeration).
如果您想做一些特别需要枚举的事情(例如,出于某种原因使用 EnumMap 或 EnumSet),则第二种形式可能很有用;否则,我会选择第一个(因为使用该方法,您还可以使用非枚举类型,或者只是枚举的一个子集)。
回答by Michael Borgwardt
Note that I know that the enums in question will all have that getDisplayValue method, but unfortunately they don't share a common type that defines it (and I can't introduce one), so I guess that will have to be accessed reflectively...?
请注意,我知道有问题的枚举都将具有该 getDisplayValue 方法,但不幸的是,它们不共享定义它的通用类型(我不能介绍一个),因此我想必须以反射方式访问它。 ..?
You are guessing correctly. An alternative would be to have the enums all implement toString()
by returning the display value - but if you can't have them implement an interface then I suppose that's not possible either.
你猜对了。另一种方法是toString()
通过返回显示值来实现所有枚举- 但如果你不能让它们实现一个接口,那么我想这也是不可能的。
回答by John Calsbeek
You can stick this method in some utility class:
您可以将此方法粘贴到某些实用程序类中:
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.
}
}
Source: Examining Enums
来源:检查枚举
回答by Nick Holt
I'd use a java.util.ResourceBundle
with a bundle file that maps to the toString
(and maybe class name) values of your enums so your code then becomes something like:
我会使用一个java.util.ResourceBundle
带有映射到toString
枚举(可能还有类名)值的包文件,这样你的代码就会变成这样:
bundle.getString(enum.getClass().getName() + enum.toString());
回答by Yishai
Here is how I would suggest going about it:
这是我建议的方法:
First a helper method and static inner class in a utility class somewhere:
首先是某个实用程序类中的辅助方法和静态内部类:
@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();
}
}
}
And then put that together with your enum:
然后把它和你的枚举放在一起:
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;
}
If performance is an issue aroung generating so many proxy objects, then I would go along the path of making a mutable class that implements DisplayableEnum that can change with each enum constant (kind of a flyweight pattern) and have an invocation handler there that is more flexible about its real object and invokes the right one on every pass through the loop.
如果性能是生成这么多代理对象的一个问题,那么我将沿着创建一个可变类的路径来实现 DisplayableEnum,该类可以随每个枚举常量(一种享元模式)而改变,并在那里有一个调用处理程序灵活处理其真实对象,并在每次循环时调用正确的对象。
回答by McDowell
This approach avoids reflection:
这种方法避免了反射:
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;
}
...but does require a separate type for everything you want to display:
...但确实需要一个单独的类型来显示您想要显示的所有内容:
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));
}
In this case, an anonymous type is used, but it could be a stateless singleton or somesuch.
在这种情况下,使用匿名类型,但它可以是无状态单例或类似的类型。
回答by Luigi Antonini
I edited in this way method on first response and it work without problem and without implement any interface
我在第一次响应时以这种方式编辑方法,它可以正常工作并且没有实现任何接口
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;
}
回答by Latency
Come on guys.. its not that hard. Im using string comparison.. but you can just compare the object type if you want.
来吧伙计们..没那么难。我使用字符串比较......但如果你愿意,你可以只比较对象类型。
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;
}