首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带Enum的Java泛型;返回类型?

带Enum的Java泛型;返回类型?
EN

Stack Overflow用户
提问于 2022-04-29 15:27:17
回答 3查看 174关注 0票数 0

我在Java程序中编写了这个函数:

代码语言:javascript
复制
public <T extends Enum<T>> T getEnum(Class<T> enumClass, String name) {
    String value = get(name);
    if (value == null) return null;
    if (value.length() == 0) return null;
    return Enum.valueOf(enumClass, value);
}

我就是这么叫它的:

代码语言:javascript
复制
CollegeDegreeType degreeType = model.getEnum(CollegeDegreeType.class, "degree_type");

它可以工作,但我觉得我应该能够省略第一个参数,并从返回类型(结果被分配到的类型)推断它。这个是可能的吗?如果是的话,语法是什么?

EN

回答 3

Stack Overflow用户

发布于 2022-05-01 21:09:55

让我问你:如果没有仿制药,你会怎么做?如果没有泛型就无法做到这一点,那么也不能用泛型来完成。泛型在编译时被擦除,在运行时不存在(除了类、字段和方法声明中的泛型可以在运行时从类文件中检查,但在这里不相关)。所以,如果它可以用泛型来完成,你必须能够擦除它并且不使用泛型。

删除的原始方法如下所示:

代码语言:javascript
复制
public Enum getEnum(Class enumClass, String name) {
    String value = get(name);
    if (value == null) return null;
    if (value.length() == 0) return null;
    return Enum.valueOf(enumClass, value);
}

但你想做这样的事:

代码语言:javascript
复制
public Enum getEnum(String name) {
    String value = get(name);
    if (value == null) return null;
    if (value.length() == 0) return null;
    return ???
}

这是不可能的即使Java没有泛型,Enum.valueOf()仍然需要在运行时使用enumClass,因为它知道在哪个枚举中查找名称。实际上,可以有多个同名常量的枚举,而EnumA.SOME_CONSTANT在运行时将是一个与EnumB.SOME_CONSTANT不同的对象,那么它如何知道您想要哪个对象呢?它不能。

也许您认为泛型方法会以某种方式传递其泛型参数(这里是T)。但这不是一般方法的意思。当您在T上编写泛型方法时,这意味着您的方法的运行时字节码对于任何T (在T的范围内)都必须是正确的,而不知道T是什么。调用方可以期望您的方法在某种程度上只起作用,不管T是什么(实际上,调用方甚至可能不知道T是什么)。

票数 2
EN

Stack Overflow用户

发布于 2022-04-29 18:16:09

它可以工作,但我觉得我应该能够省略第一个参数,并从返回类型推断它。

您将无法在运行时提供T,除非您有以下任何一种:

  • 方法参数,类型为T (您想省略它);
  • 或实例T是方法调用的结果;
  • 是枚举类型的硬编码实例(坦率地说,这是毫无意义的)。

f 212

示例(第二个和第三个选项)

代码语言:javascript
复制
public static <T extends Enum<T>> T getEnum(String name) {
    T myEnum = (T) getHardCodedEnumMember();
    
    return (T) Enum.valueOf(myEnum.getClass(), name);
}

public static <T extends Enum<T>> T getHardCodedEnumMember() {
    return (T) DayOfWeek.SATURDAY;
}

这是可行的,但我想这不是您的意图,所以我必须坚持将Class实例作为参数传递。

另一件需要考虑的事情是,您列出的方法可能有三个结果:

  • 它可以返回枚举元素;
  • 可以返回null值;
  • 或引发异常。

如果没有具有匹配名称的枚举成员,valueOf()将抛出IllegalArgumentException。而且,预先检查null和空字符串不会将您从它中拯救出来。同时,您需要处理一个可空的结果。

您可以采取另一种方法,消除运行时异常,并将可能的结果减少为包含结果的空可选或可选结果。为此,您可以使用EnumSet.allOf()检索所有特定枚举类型的内容。

代码语言:javascript
复制
public <T extends Enum<T>> Optional<T> getEnum(Class<T> enumClass, String name) {
    String value = get(name);
    if (value == null || value.length() == 0) {
        return Optional.empty();
    }
    
    return EnumSet.allOf(enumClass).stream()
            .filter(element -> element.name().equals(value))
            .findFirst();
}
票数 0
EN

Stack Overflow用户

发布于 2022-04-29 19:43:31

由于泛型类型擦除--只有在编译时才知道T类型,这将阻止您在运行时访问它的类,而这是Enum.valueOf所需的。

请参阅https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

在某些情况下,可以从技术上检索泛型类型,请参阅Get generic type of class at runtime

但我不认为这会提高代码的质量。在我看来,你目前获得这门课的解决方案是一种很好的方法。

一个可以工作的示例实现,但在我看来是hacky* (*由于未检查的转换、反射和匿名类实现/扩展语法要求):

代码语言:javascript
复制
    public abstract class MyEnumParser<T extends Enum<T>> {
        public T parseEnum(String value) {
            return Enum.valueOf(getEnumClass(), value);
        }

        private Class<T> getEnumClass() {
           return (Class<T>) ((ParameterizedType) getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[0];
        }
    }

用法:

代码语言:javascript
复制
MyEnumParser<CollegeDegreeType> parser = new MyEnumParser<>() {};
System.out.println(parser.parseEnum("degree_type"));
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72060257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档