web-dev-qa-db-ja.com

Enum.valueOf(Class <T> enumType、String name)質問

動的な列挙型ルックアップに関連するコンパイルエラー(「バインドの不一致:...」)を回避しようとしています。

基本的に私はこのようなことを達成したいと思います:

String enumName = whatever.getEnumName();
Class<? extends Enum<?>> enumClass = whatever.getEnumClass();
Enum<?> enumValue = Enum.valueOf(enumClass, enumName);

私が何をするにせよ、私はいつもそのコンパイルエラーに終わります。正直なところ、ジェネリックスと列挙型は私にとってかなり困惑しています...

ここで何が悪いのですか?

34
Tom

(型またはメソッドのシグネチャのいずれかを介して)型変数にアクセスできない限り、これは正確に機能しないと思います。問題は、Enum.valueOfのメソッドシグネチャです。

public static <T extends Enum<T>> T valueOf(
    Class<T> enumType,
    String name
);

型変数なしでTを取得する方法はありません。しかし、コンパイラの警告を抑制したい場合は、次のように実行できます。

public enum King{
    ELVIS
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(final String[] args){
    final Class<? extends Enum> enumType = King.class;
    final Enum<?> theOneAndOnly = Enum.valueOf(enumType, "ELVIS");
    System.out.println(theOneAndOnly.name());
}

出力:

エルビス

31

問題はClass<? extends Enum<?>>にあります。 E extends Enum<E>が必要ですが、2つの異なるワイルドカードがあるため、取得できません。

したがって、ジェネリックパラメーターを導入する必要があります。これは、メソッドを呼び出すことで導入できます。

enum MyEnum {
    ME;
}

public class EnName {
    public static void main(String[] args) {
        Enum<?> value = of(MyEnum.class, "ME");
        System.err.println(value);
    }
    private static <E extends Enum<E>> E of(Class<E> clazz, String name) {
        E value = Enum.valueOf(clazz, name);
        return value;
    }
}

しかし、反射はごちゃごちゃしていて、ごくまれに望みのものです。しないでください。

24

実際には別のアプローチがあります。Class.getEnumConstantsを使用して、同じ型の問題がないEnum.valueOfの独自の実装をロールできます。欠点は、一般的なEnum<?>が返されることです。ただし、取得したタイプがわかっていれば、とにかくEnum.valueOfを使用できます。

private static Enum<?> findEnumValue(Class<? extends Enum<?>> enumType, String value) {
    return Arrays.stream(enumType.getEnumConstants())
                 .filter(e -> e.name().equals(value))
                 .findFirst()
                 .orElse(null);
}

(私のバージョンでは、nullをスローするのではなく、そのような定数がない場合はIllegalArgumentExceptionを返しますが、それは簡単な変更です。

6
Sbodd