【问题标题】:How do I retrieve the interface of the constants from a generic enum type?如何从泛型枚举类型中检索常量的接口?
【发布时间】:2014-08-12 11:14:30
【问题描述】:

我正在为一个必须处理泛型枚举的类设计一个方法的参数(我的意思是,该类可以是库的一部分,并且用户将为该类方法提供用户定义的枚举,其具体类型对于正在设计的类来说是未知的 - 我们称这个类为“库类”)。

我想在语法上尽可能地限制该参数类型,以便只允许正确的类型。

我的要求是类型必须是实现特定接口的枚举,EnumKind:

public interface EnumKind {

    String getText();

}

到目前为止,我已经能够写出这种类型了:

Class<? extends Enum<? extends EnumKind>> t;

有了该声明,并拥有这些:

public enum MyEnumKind implements EnumKind {

    // ...

    public String getText() {
        // TODO Auto-generated method stub
        return null;
    }

}

public class MyClassKind implements EnumKind {

    @Override
    public String getText() {
        // TODO Auto-generated method stub
        return null;
    }

}

public enum MyEnum {

}

只有t = MyEnumKind.class 是正确的,其他赋值(如t = MyClassKind.class)被编译器拒绝,这是我想要的行为。

现在,在我的方法体中,我可以成功地做到:

Enum<? extends EnumKind>[] tt = t.getEnumConstants();

但我还没有找到直接检索EnumKinds 的方法,以访问每个枚举常量getText() 方法。

如何在没有显式转换的情况下获得 EnumKind 元素数组?该类型声明是我能写的最好的,还是我可以改进它?
如果无法避免显式转换,我是否可以 100% 安全地确保类型声明将避免任何ClassCastException(或任何类似异常)被抛出的可能性?


编辑


我会详细说明一下,因为我遇到了另一个障碍。

EnumKind 接口要求枚举将字符串与每个枚举常量相关联,即:

public enum MyEnumKind implements EnumKind {

    A("#A#"),
    B("#B#"),
    C("#C#");

    private String text;

    private Token(String text) {
        this.text = text;
    }

    @Override
    public String getText() {
        return text;
    }

}

接下来,我要检索与特定字符串对应的枚举常量,例如

? enumValue = getValueFor("#A#");

enumValue(它的类型应该是什么?)这里应该代表MyEnumKind.A,不需要知道实际值,因为enumValue会被传递给另一个方法。

我已经设计了这样的库类(在@JoopEggen 回答之后):

public class EnumManager<T extends Enum<?> & EnumKind> {

    private Class<T> en;

    public EnumManager(Class<T> en) {
        this.en = en;
    }

   protected ? getValueFor(String s) {
        for(EnumKind ek : en.getEnumConstants()) {
            if(s.equals(ek.getText())) {
                // I found the enum constant I need... and now?
                return ? ;
            }
        }

    }

    //...

我应该如何完成该代码?

【问题讨论】:

    标签: java generics interface enums


    【解决方案1】:

    要使用.getText T 也必须“扩展” EnumKind。 Enum 包装器不提供此功能。

    public static class MyC<T extends Enum<?> & EnumKind> {
        public void f(T t) {
            System.out.println("-> " + t.getText());
        }
    }
    

    在您上方看到extends CLASS &amp; INTERFACE。额外的绑定必须是一个接口。


    问题编辑后:

    protected T getValueFor(String s) {
        for (T t : en.getEnumConstants()) {
            if (s.equals(ek.getText())) {
                return t;
            }
        }
    }
    

    使用静态 HashMap 可能会更好。

    我担心整个代码可能会过度设计。最好遵循一个简单的设计,有太多的工件。

    你想要一个像 AbstractEnum 这样的东西,从

    public enum EnumABC {
        A("a"), B("b"), C("c");
    
        private static final Map<String, EnumABC> textToEnum = new HashMap<>();
        static {
            for (EnumABC value : values()) {
                EnumABC oldValue = textToEnum.put(value.text, value);
                if (oldValue != null) {
                    throw new IllegalArgumentException(
                            String.format("Enum %s: same value %s for %s and %s",
                                    value.getClass().getName(),
                                    value.text,
                                    oldValue,
                                    value));
                }
            }
        }
    
        public static EnumABC fromText(String text) {
            EnumABC value = textToEnum.get(text);
            return value;
        }
    
        private final String text;
    
        private EnumABC(String text) {
            this.text = text;
        }
    }
    

    祝你好运。

    更简单:

    @Override
    public String getText() {
        String s = toString();
        return "#" + s.replace('_', ' ') + "#";
    }
    

    【讨论】:

    • 嗯...如果我理解你的意思,你建议将泛型类型声明移到类声明中,而不是使用特定的参数类型,对吗?
    • 不,这只是我为了使用 getText 而进行的测试。 (我不完全确定您对t 的使用。)想提一下泛型的&amp;
    • 我已经添加了关于我想要做什么的解释。可以看看吗?
    【解决方案2】:

    是的,这是安全的,因为 Number[]Integer[] 的超类(与 List&lt;Number&gt; 不是List&lt;Integer&gt; 的超类相反)。

    所以如果tt的声明组件类型(tt的元素的声明类型)实现EnumKind,这样的元素数组扩展EnumKind[]因此这是安全的:

    EnumKind[] tt = (EnumKind[]) t.getEnumConstants(); // Casting to superclass: OK
    

    您不会收到任何警告,因为这是安全的,而且您永远不会收到 ClassCastException

    【讨论】:

    • 谢谢,我正在调查 Joop 的回答,看看能否简化代码。
    • 我已经添加了关于我想要做什么的解释。可以看看吗?
    • "如果tt 的元素实现EnumKind,则此类元素的数组将扩展EnumKind[]" 这不是真的。 EnumKind[] tt = (EnumKind[])new Object[]{ MyEnumKind.A }; 将失败,即使所有元素都实现了 EnumKind
    • 是的,我的意思是声明的数组组件类型。已编辑。
    【解决方案3】:

    泛型方法怎么样:

    static <T extends Enum<T> & EnumKind> T helper(Class<T> t, String s) {
        EnumKind[] tt = t.getEnumConstants();
        // do what you need to do here
    }
    

    【讨论】:

    • 我需要返回实际的枚举常量,通过将字符串与返回的getText() 进行比较找到。在您编写的 sn-p 中,我无法返回,例如 tt[0],我得到“类型不匹配:无法从 TokenEnum 转换为 T”。但是你给了我一个提示,我在上面的代码中用for(T ek : en.getEnumConstants()) 替换了for(EnumKind ek : en.getEnumConstants()):在循环中我可以做ek.getText(),我可以返回ek
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    相关资源
    最近更新 更多