列挙値をHTMLでの表示に適した文字列のリストに変換するヘルパーメソッドがいくつかあります<select>
要素。これらを単一のポリモーフィックメソッドにリファクタリングすることは可能かどうか疑問に思いました。
これは私の既存の方法の1つの例です:
/**
* 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
メソッドがあることを私は知っていますが、残念ながらそれらはそれを定義する共通の型を共有していないので(そして私はそれを導入できません)、それは反射的にアクセスする...?
助けてくれてありがとう。
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
メソッドなしで列挙型を持つことができるため、これは完全にタイプセーフではありません。
このメソッドは、いくつかのユーティリティクラスに固定できます。
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つあります。最初の(より単純で、したがってより良い)方法は、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を自動的にサブクラス化するという事実を使用することもできます。したがって、あなたの例では(免責事項:I thinkこれはコンパイルされますが、実際には試していません):
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);
}
この2番目の形式は、特に列挙を必要とする何かを実行したい場合に役立ちます(たとえば、何らかの理由でEnumMapまたはEnumSetを使用します)。それ以外の場合は、最初のものを使用します(この方法では、列挙型ではない型、または列挙型のサブセットのみを使用することもできます)。
このアプローチは反射を回避します:
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));
}
この場合、匿名型が使用されますが、ステートレスシングルトンなどである可能性があります。
列挙型のtoString
(およびおそらくクラス名)値にマップするバンドルファイルでJava.util.ResourceBundle
を使用すると、コードは次のようになります。
bundle.getString(enum.getClass().getName() + enum.toString());
これが私がそれについて行くことを提案する方法です:
まず、どこかのユーティリティクラスのヘルパーメソッドと静的内部クラス:
@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を実装する可変クラスを作成し、そこにさらに多くの呼び出しハンドラーを含めるというパスに沿って進みます。実際のオブジェクトについて柔軟であり、ループを通過するたびに適切なオブジェクトを呼び出します。
問題の列挙型にはすべてそのgetDisplayValueメソッドがあることはわかっていますが、残念ながら、それらはそれを定義する共通の型を共有していないため(導入できません)、反射的にアクセスする必要があると思います。 ..?
あなたは正しく推測しています。別の方法は、表示値を返すことによってすべての列挙型にtoString()
を実装させることですが、インターフェイスを実装させることができない場合は、それも不可能だと思います。
私は最初の応答でこの方法でメソッドを編集しました、そしてそれは問題なくそしてインターフェースを実装することなく動作します
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;
}