简体   繁体   English

如何从通用枚举类型检索常量的接口?

[英]How do I retrieve the interface of the constants from a generic enum type?

I'm designign an argument of a method, for a class that has to handle generic enumerations (I mean, the class could be part of a library, and the user will provide that class method with a user-defined enumeration, whose specific type is not known to the class being designed - let's call this class the "library class"). 我为一个必须处理通用枚举的类指定一个方法的参数(我的意思是,该类可能是库的一部分,并且用户将为该类方法提供用户定义的枚举,其特定类型对于正在设计的类是未知的-我们将此类称为“库类”)。

I want to syntactically constrain that argument type as much as possible so that only correct types will be allowed. 我想在语法上尽可能地限制该参数类型,以便只允许使用正确的类型。

My requirements are that the type must be an enumeration that implements a specific interface, EnumKind : 我的要求是,该类型必须是实现特定接口EnumKind的枚举:

public interface EnumKind {

    String getText();

}

So far I have been able to write this type: 到目前为止,我已经能够编写这种类型的代码:

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

With that declaration, and having these: 有了该声明,并具有以下内容:

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 {

}

Only t = MyEnumKind.class is correct, other assignments (like t = MyClassKind.class ) are rejected from the compiler, which is the behaviour I want. 只有t = MyEnumKind.class是正确的,其他分配(例如t = MyClassKind.classt = MyClassKind.class编译器拒绝,这是我想要的行为。

Now, in the body of my method I can succesfully do: 现在,在我的方法主体中,我可以成功执行以下操作:

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

But I haven't found a way to directly retrieve the EnumKind s, to get access to each enumeration constant getText() method. 但是我还没有找到一种直接检索EnumKind的方法来访问每个枚举常量getText()方法。

How do I get an array of EnumKind elements without explicit casting? 我如何在没有显式转换的情况下获取EnumKind元素数组? Is that type declaration the best I can write, or can I improve it? 该类型声明是我能写的最好的,还是可以改进它?
If explicit casting can't be avoided, can I be 100% safe that the type declaration will avoid any possibility of any ClassCastException (or any similar exception) being thrown? 如果无法避免显式强制转换,那么我可以100%安全地声明类型声明将避免引发任何ClassCastException (或任何类似异常)的可能性吗?


EDIT 编辑


I'll elaborate a bit more, since I'm hitting another road block. 我将进一步说明,因为我遇到了另一个障碍。

The EnumKind interface requires the enumeration to associate a string to a each enum constant, ie: 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;
    }

}

Next, I want to retrieve the enum constant that corresponds to a specific string, eg 接下来,我想检索对应于特定字符串的枚举常量,例如

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

enumValue (what should be its type?) should represent MyEnumKind.A here, and there's no need to know the actual value, since enumValue will be passed to another method. enumValue (它的类型应该是什么?)在这里应该表示MyEnumKind.A ,并且不需要知道实际值,因为enumValue将传递给另一个方法。

And I've designed my library class like this (after @JoopEggen answer): 我已经像这样设计了我的图书馆类(在@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 ? ;
            }
        }

    }

    //...

How should I complete that code? 我应该如何完成该代码?

Yes, it's safe since Number[] is a superclass to Integer[] (contrary to List<Number> not being a superclass of List<Integer> ). 是的,这是安全的,因为Number[]Integer[]的超类(与List<Number> 并非 List<Integer>的超类相反)。

So if the declared component type of tt (the declared type of the elements of tt ) implements EnumKind , an array of such elements extends EnumKind[] therefore this is safe: 因此,如果所宣称的组件类型tt (声明的类型的元件中的tt )实现EnumKind ,这种元件的阵列延伸EnumKind[]因此,这是安全的:

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

You get no warnings because this is safe and you will never get ClassCastException . 您不会收到任何警告,因为这是安全的,并且永远不会收到ClassCastException

To use .getText T must "extend" EnumKind too. 要使用.getText T也必须“扩展” EnumKind。 The Enum wrapper does not provide this. 枚举包装器不提供此功能。

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

Above you see extends CLASS & INTERFACE . 在上方可以看到extends CLASS & INTERFACE The extra bound must be to an interface. 额外的限制必须是接口。


After question edit: 问题编辑后:

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

Maybe better with a static HashMap. 使用静态HashMap可能更好。

I am afraid, that the entire code might become overdesigned. 恐怕整个代码可能会过度设计。 It would be better to follow a simple design, with too many artifacts. 遵循带有太多工件的简单设计会更好。

You want something like an AbstractEnum, abstracting from 您想要类似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;
    }
}

Good luck. 祝好运。

Simpler: 更简单:

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

How about a generic method: 通用方法如何:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM