如果我有一组枚举,并希望让它们都实现一个接口,这是一般的正确方法吗?
枚举:
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;
}
}
接口:
public interface SkillType {
SkillType fromValue(String value);
int getUntrainedPenalty();
}
持有班级(我怀疑这是不对的):
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);
不起作用,这是我的下一个猜测。
我查看过这些教程(这是我迄今为止的方式),我似乎无法看到如何进一步。如何使此代码生效?
答案 0 :(得分:3)
你的问题在于SkillType
- 它应该是通用的:
public interface SkillType<T extends SkillType<T>> {
T fromValue(String value);
int getUntrainedPenalty();
}
这称为自引用泛型参数。
如果没有此自引用,您可以从fromValue()
方法返回不同类的实例。
以下是它的外观:
public enum MentalSkill implements SkillType<MentalSkill> {
public MentalSkill fromValue(...) {}
...
}
和
public class SkillsSet<T extends SkillType<T>> {
...
}
一旦你做出这个改变,它应该全部落实到位。
答案 1 :(得分:2)
问题出现在我的T t中,显然会给出一个NPE,因为我没有实例化我的推断类型。问题在于我不能,因为它是一个枚举。
实际上,即使它是一个常规的&#34;类!除非您将Class<T>
对象作为显式参数传入,否则无法在泛型类中创建参数类型的实例。
如果是enum
,您需要传入enum
的值,或者传递Class<T>
enum
并使用静态Enum<T>.valueOf(Class<T>, String)
方法进行查找。
需要这样的东西......
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
地图对我来说并没有多大意义。你为什么要使用&#34;未经训练的处罚&#34;一个T
实例初始化所有技能的技能值?如果它更像是这样的话会更有意义:
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
:
SkillSet<MentalSkill> st = new SkillSet<>(new String[]{"COMPUTER", "OCCULT"},
MentalSkill.class);
(注意:这是所有未编译/未经测试的代码!!!)
请注意,如果您有一个fromValue
对象并且可以调用Class<T>
,则不需要Enum<T>.valueOf(...)
方法。它在概念上有点奇怪。 (您正在使用T
的一个实例作为T
的其他实例的工厂对象。)
答案 2 :(得分: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;
}
}
...
}
getEnumValue
有两种不同的版本。我建议在getEnumConstants
参数上使用enumClass
方法的版本。它还允许您处理fromValue
中可能通常与枚举的valueOf
函数不匹配的特殊情况。
实例化上述工作
SkillsSet<MentalSkill> = new SkillsSet<MentalSkill>(new String[] {"COMPUTER", "CRAFTS"}, MentalSkill.class);
答案 3 :(得分:1)
这里
@Override
public SkillType fromValue(String value) {
return valueOf(value);
}
方法valueOf(value)
是静态方法,它与枚举实例无关,这意味着您无法一般性地调用它。你能做什么:
public class SkillsSet<T extends SkillType> {
T t = MentalSkill.values()[0];
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);
}
}
或更清楚
public class SkillsSet<T extends SkillType> {
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(MentalSkill.valueOf(skillString), 0);
}
}
如果您计划使用更多实现SkillType
的枚举类型,那么您需要一个超过这些类型的工厂。但是在这种情况下,enum可能不是最好的设计决策。