Enum.valueOf(Class<T> enumType, String name) 问题

Posted

技术标签:

【中文标题】Enum.valueOf(Class<T> enumType, String name) 问题【英文标题】:Enum.valueOf(Class<T> enumType, String name) question 【发布时间】:2010-10-25 11:32:42 【问题描述】:

我正在尝试解决与动态枚举查找相关的编译错误(“绑定不匹配:...”)。

基本上我想实现这样的目标:

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

无论我做什么,我总是以编译错误告终。老实说,泛型和枚举对我来说非常令人难以置信......

我在这里做错了什么?

【问题讨论】:

【参考方案1】:

我认为除非您可以访问类型变量(通过类型或方法签名),否则它不会完全像这样工作。问题是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());

输出:

猫王

【讨论】:

问题是关于枚举、泛型和反射。如果你忽略泛型,那有什么意义呢?特别是像Class&lt;? extends Enum&gt;这样的“稀有类型”。 重点是 a) 如果没有辅助方法或类型,它就无法工作,b) 我们确信任何 Class&lt;? extends Enum&gt; 也将满足 Class&lt;E extends Enum&lt;E&gt;&gt; (因为这就是 enum 的方式类工作)即使没有类型变量就无法检查。 @SuppressWarnings 是一个注释,只有当你知道自己在做什么时才应该使用,而且我知道。【参考方案2】:

问题在于Class&lt;? extends Enum&lt;?&gt;&gt;。我们想要E extends Enum&lt;E&gt;,但我们无法得到它,因为我们有两个不同的通配符。

所以我们需要引入一个泛型参数,可以通过调用方法引入:

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;
    

但是反射是肮脏的,很少是你想要的。不要这样做。

【讨论】:

感谢您的解释,它对进一步了解发生的事情有很大帮助。因为在我的特定用例中,我没有任何东西可以作为通用参数提供,所以我将寻求 seanizer 的答案。【参考方案3】:

实际上还有另一种方法:您可以使用Class.getEnumConstants 并滚动您自己的Enum.valueOf 实现,它没有相同的类型问题。缺点是你会得到一个通用的Enum&lt;?&gt;——但如果你知道你输入的是什么类型,你无论如何都可以使用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,但这是一件微不足道的改变。

【讨论】:

【参考方案4】:

谢谢@Sbodd 的答案。我举了这个例子,添加了我们从标准 anEnum.valueOf("ELVIS") 中知道的异常,并以我们可以像这样使用它的方式更改了通用方法 args:

       King king = findEnumValue(King.class, "ELVIS");
       System.out.println(String.valueOf(king)); 

方法:

/**
 * Returns the @link Enum for the given @link Enum#name() String of the given enum type. 
 * <p>
 * Example: 
 * <pre>
 * @code     
      King king = findEnumValue(King.class, "ELVIS");
      System.out.println(String.valueOf(king));
 * 
 * </pre>
 * @param enumType the class of the enum, e.g. @code King.class. 
 * 
 * @param enumName the @link Enum#name(), e.g. "ELVIS". 
 * 
 * @return the enum instance or an exception. 
 * 
 * @throws IllegalArgumentException if the given @code enumName is no enum constant in the given @code enumType. 
 */
public static <T extends Enum<T>> T findEnumValue(Class<T> enumType, String enumName) throws IllegalArgumentException 
    T result = Arrays.stream(enumType.getEnumConstants())
                 .filter(e -> e.name().equals(enumName))
                 .findFirst()
                 .orElse(null);
    if(result == null)
    
        throw new IllegalArgumentException(
                    "No enum constant " + enumType.getCanonicalName() + "." + enumName);
    
    else
    
        return result;
    


public enum King
    ELVIS

【讨论】:

以上是关于Enum.valueOf(Class<T> enumType, String name) 问题的主要内容,如果未能解决你的问题,请参考以下文章

枚举类型休眠 3

spring源码读书笔记

心知天气调用

java泛型T和class.getName一样吗?

Build:Class 'Subject<T>' 错误地扩展了基类 'Observable<T>'

在java中t.getClass为啥返回的是Class<gt;而不是Class<T>