首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有界面的Enums -如何泛化?

具有界面的Enums -如何泛化?
EN

Stack Overflow用户
提问于 2014-08-08 07:08:30
回答 4查看 1.3K关注 0票数 10

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

Enum:

代码语言:javascript
复制
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;
    }
}

接口:

代码语言:javascript
复制
public interface SkillType {

SkillType fromValue(String value);  
int getUntrainedPenalty();
}

(在我被停课的地方,这是不完全正确的):

代码语言:javascript
复制
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);
    }
}

这个问题出现在我的T t中,它显然会给出一个NPE,因为我没有实例化我的推断类型。问题是我做不到,因为这是个谜。

Java的说法是什么?“一般使用这个接口来访问枚举方法。”加入skills.put(T.fromValue(skillString), 0);不起作用,这是我的下一个猜测。

我已经看过了教程(这就是我是如何做到这一点的),我似乎看不出如何得到更多的东西。我怎样才能让这个代码工作?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-08-14 21:32:24

一种解决方案是通过使用反射。不过,我不确定是否还有其他的解决方案看起来更干净。但是,下面的代码是编译和运行的。

代码语言:javascript
复制
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; 
        }
    }


    ...
}

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

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

票数 2
EN

Stack Overflow用户

发布于 2014-08-08 08:11:16

您的问题在于SkillType --它应该是通用的:

代码语言:javascript
复制
public interface SkillType<T extends SkillType<T>> {

    T fromValue(String value);  

    int getUntrainedPenalty();
}

这称为自引用泛型参数。

如果没有这个自引用,您可以从fromValue()方法返回一个不同类的实例。

下面是它的样子:

代码语言:javascript
复制
public enum MentalSkill implements SkillType<MentalSkill> {
    public MentalSkill fromValue(...) {}
    ...
}

代码语言:javascript
复制
public class SkillsSet<T extends SkillType<T>> {
    ...
}

一旦你做了这个改变,一切都应该就位。

票数 3
EN

Stack Overflow用户

发布于 2014-08-08 07:21:40

这个问题出现在我的‘t中,这显然会给出一个NPE,因为我没有实例化我的推断类型。问题是我做不到,因为这是个谜。

事实上,即使这是一个“常规”的课程,你也不能!无法在泛型类中创建参数类型的实例.除非您将Class<T>对象作为显式参数传入。

enum的情况下,您需要传入enum的值,或者传入enumClass<T>并使用静态Enum<T>.valueOf(Class<T>, String)方法进行查找。

像这样的东西是必要的..。

代码语言:javascript
复制
public class SkillsSet<T extends SkillType> {
    T t;
    Map<T,Integer> skills = new HashMap<>();
    public SkillsSet(String[] skills, T t) {
        this.t = t;
        for (String string : skills) {
            addSkill(string, t.getUntrainedPenalty());
        }
    }
    private void addSkill(String skillString, Integer value) {
        skills.put((T) t.fromValue(skillString), value);
    }
}

但是skills地图对我来说没有多大意义。为什么要对一个T实例使用“未经训练的惩罚”来初始化所有技能的技能值?如果它更像这样的东西,那就更有意义了:

代码语言:javascript
复制
public class SkillsSet<T extends SkillType> { 
    private Map<T,Integer> skills = new HashMap<>();
    public SkillsSet(String[] skills, Class<T> enumClass) {
        for (String string : skills) {
            T t = Enum<T>.valueOf(enumClass, string);
            skills.put(t, t.getUntrainedPenalty());
        }
    }
    // Methods for accessing / updating the skills map information ...
}

您可以像这样实例化一个SkillSet

代码语言:javascript
复制
SkillSet<MentalSkill> st = new SkillSet<>(new String[]{"COMPUTER", "OCCULT"},
                                          MentalSkill.class);

(注:这是所有未编译/未经测试的代码!)

注意,如果您有一个fromValue对象,并且您可以调用Enum<T>.valueOf(...),那么您的Enum<T>.valueOf(...)方法是不必要的。这在概念上有点奇怪。(您正在使用T的一个实例作为T其他实例的工厂对象。)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25197783

复制
相关文章

相似问题

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