Enum.valueOf(ClassenumType, String name) 问题

发布于 2024-09-28 14:46:17 字数 338 浏览 3 评论 0原文

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

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

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

无论我做什么,我总是会遇到编译错误。老实说,泛型和枚举对我来说相当令人难以置信......

我在这里做错了什么?

I am trying to get around a compile error ("Bound mismatch: ...") relating to dynamic enum lookup.

Basically I want to achieve something like this:

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

Whatever I do, I always end up with that compile error. Honestly, generics and enums are quite mindboggling to me...

What am I doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

活雷疯 2024-10-05 14:46:17

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

输出:

埃尔维斯

I think it won't work exactly like this unless you have access to a type variable (through either a type or method signature). The problem is the method signature of Enum.valueOf:

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

There's no way to get a T without a type variable. But you can do it like this if you're willing to suppress some compiler warnings:

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());
}

Output:

ELVIS

眼眸 2024-10-05 14:46:17

问题出在 Class>。我们想要 E extends Enum,但我们无法得到它,因为我们有两个不同的通配符。

因此,我们需要引入一个通用参数,可以通过调用方法来引入:

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;
    }
}

但是反射很糟糕,而且很少是您想要的。不要这样做。

The problem is with Class<? extends Enum<?>>. We want E extends Enum<E>, but we can't get that because we have two distinct wildcards.

So we need to introduce a generic parameter, possible introduced by calling a method:

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;
    }
}

But reflection is mucky and very rarely what you want. Don't do it.

心房的律动 2024-10-05 14:46:17

实际上有一种替代方法:您可以使用 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,但这是一个微不足道的更改。

There's actually an alternate approach: you can use Class.getEnumConstants and roll your own implementation of Enum.valueOf that doesn't have the same type problems. The downside is you get back a generic Enum<?> - but if you knew what type you had coming in, you would be able to use Enum.valueOf anyway.

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

(Note that my version returns null if there's no such constant rather than throwing an IllegalArgumentException, but that's a trivial thing to change.

小红帽 2024-10-05 14:46:17

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

       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
}

Thx @Sbodd for the answer. I took this example, added the exception we know from the standard anEnum.valueOf("ELVIS") and changed the generic method args in a way that we can use it like this:

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

Method:

/**
 * 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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文