带界面的枚举 - 一般如何做?

Pur*_*ret 9 java enums interface

如果我有一组枚举,并希望让它们都实现一个接口,这是一般的正确方法吗?

枚举:

public enum MentalSkill implements SkillType {
    ACADEMICS,COMPUTER,CRAFTS,INVESTIGATION,MEDICINE,OCCULT,POLITICS,SCIENCE;
    private static final int UNTRAINED_PENALTY = -3;
    @Override
    public SkillType fromValue(String value) {
        return valueOf(value);
    }
    @Override
    public int getUntrainedPenalty() {
        return UNTRAINED_PENALTY;
    }
}
Run Code Online (Sandbox Code Playgroud)

接口:

public interface SkillType {

SkillType fromValue(String value);  
int getUntrainedPenalty();
}
Run Code Online (Sandbox Code Playgroud)

持有班级(我怀疑这是不对的):

public class SkillsSet<T extends SkillType> {
    T t;
    Map<T,Integer> skills = new HashMap<>();
    public SkillsSet(String[] skills) {
        for (String string : skills) {
            addSkill(string,t.getUntrainedPenalty());
        }
    }
    private void addSkill(String skillString,Integer value) {
        skills.put((T) t.fromValue(skillString), 0);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题出现在我的T t内容中显然会给出一个NPE,因为我没有实例化我的推断类型.问题是我不能,因为它是一个枚举.

什么是Java方式说'使用此接口一般访问枚举方法'.投掷skills.put(T.fromValue(skillString), 0);不起作用,这是我的下一个猜测.

我已经查看了教程(这是我迄今为止的方式),我似乎无法看到如何进一步.如何使此代码生效?

Rob*_*der 2

一种解决方案是使用反射。我不确定是否还有其他看起来更干净的解决方案。不过,以下内容确实可以编译并运行。

public class SkillsSet<T extends SkillType> { 
    private Map<T,Integer> skills = new HashMap<>();

    // These don't have to be class members, they could be passed
    // around as parameters
    private T[] enumConstants;   // needed only for getEnumValue
    private Class<T> enumClass;  // needed only for getEnumValueAlternate

    public SkillsSet(String[] skills, Class<T> enumClass) {
        // Though all implementers of SkillType are currently enums, it is safer
        // to do some type checking before we do any reflection things
        enumConstants = enumClass.getEnumConstants();
        if (enumConstants == null)
            throw new IllegalArgumentException("enumClass is not an enum");

        for (String string : skills) {
            T t = getEnumValue(string)
            if (t == null) {
                // or use continue if you dont want to throw an exception
                throw new IllegalArgumentException();
            }
            this.skills.put(t, t.getPenalty());
        }
    }

    // These don't even need to be methods, but I separated them for clarity.
    // SuppressWarnings annotation is used since we checked types in the constructor
    @SuppressWarnings( "unchecked" )
    public T getEnumValue( String string ) {
        try {
            return (T) enumConstants[0].fromValue(string);
        }

        // If valueOf does not find a match, it throws IllegalArgumentExecption
        catch ( IllegalArgumentException e ) {
            return null;
        }
    }

    // An alternate version  of getEnumValue that does not require the 'fromValue'
    // method on the SkillType interface.
    @SuppressWarnings( "unchecked" )
    public T getEnumValueAlternate( String string ) {
        try {
            return (T) enumClass.getMethod("valueOf", String.class).invoke(null, string)
        }

        // Any reflection exceptions are treated as 'not found' on valueOf
        catch (Exception e) {
            return null; 
        }
    }


    ...
}
Run Code Online (Sandbox Code Playgroud)

有两个不同的版本getEnumValuegetEnumConstants我推荐使用参数方法的版本enumClass。它还允许您处理通常fromValue可能与枚举valueOf函数不匹配的特殊情况。

实例化上述作品 SkillsSet<MentalSkill> = new SkillsSet<MentalSkill>(new String[] {"COMPUTER", "CRAFTS"}, MentalSkill.class);