如果有人能指出我做错了什么,我将不胜感激。
我有一个接口IDoubleSource,我在Person类中实现。 有一个 LinearRegression 类,其方法采用 IDoubleSource 参数,但我将传入 Person 类。
作为 IDoubleSource 接口的一部分,必须定义一个名为 Variables 的枚举和一个名为 getDoubleValue(Enum) 的方法。 下面,我将展示我是如何在 Person 中完成此操作的,并且枚举类型用于在 getDoubleValue() 方法中指定开关大小写。
问题:
1)在LinearRegression中,有一个方法computeScore((MultiKeyCoefficient)Map,IDoubleSource),其中最后一个参数是一个接口。 我似乎无法访问 computeScore 方法中 IDoubleSource 实现实例的变量枚举,尽管将接口导入到 LinearRegression 类中。 它只是没有注册IDoubleSource具有一个名为Variables的枚举(尽管我可以很好地调用getDoubleValue()方法)。 我是否明显做错了什么,阻止我访问枚举变量?
2) Person 类中的 getDoubleValue(Enum) 方法旨在返回一个双精度值,该值取决于传递给它的枚举变量的值。 通过循环遍历 LinearRegression 类中 (MultiKeyCoefficient)Map 的键(字符串类型),我想使用这些键来指定我想要的枚举值作为参数,以便在 LinearRegression 类中获取双倍值(Enum)(我希望 getDoubleValue() 根据它在循环中收到的枚举值返回几个不同的值)。 但是,我不能使用(String)键代替预期的枚举,因为我得到了一个ClassCastExceptionjava.lang.String不能强制转换为java.lang.Enum。 如何使用映射的键来指定枚举?
我不太熟悉在 Java 中使用枚举类型,这可能是我问题的很大一部分。
现在代码详细信息:
我实现以下接口:
双源接口
public interface IDoubleSource {
public enum Variables {
Default;
}
/**
* Return the double value corresponding to the given variableID
* @param variableID A unique identifier for a variable.
* @return The current double value of the required variable.
*/
public double getDoubleValue(Enum<?> variableID);
}
通过创建类:
人员类
public class Person implements IDoubleSource {
public enum Variables {
nChildren,
durationInCouple,
ageDiff;
}
public Person() {
...
}
public double getDoubleValue(Enum<?> variableID) {
switch ((Variables) variableID) {
case nChildren:
return getNChildren();
case durationInCouple:
return (double)getDurationInCouple();
case ageDiff:
return getAgeDiff();
default:
throw new IllegalArgumentException("Unsupported variable");
}
在另一个包中,我有一个类:
线性回归类
public class LinearRegression
private MultiKeyCoefficientMap map = null;
public LinearRegression(MultiKeyCoefficientMap map) {
this.map = map;
}
....
public double score(IDoubleSource iDblSrc) {
return computeScore(map, iDblSrc);
}
public static double computeScore(MultiKeyCoefficientMap coeffMap, IDoubleSource iDblSrc) {
try {
final Map<String, Double> varMap = new HashMap<String, Double>();
for (Object multiKey : coeffMap.keySet())
{
final String key = (String) ((MultiKey) multiKey).getKey(0);
Enum<?> keyEnum = (Enum<?>) key; //Throws class cast exception
double value = iDblSrc.getDoubleValue(keyEnum);
varMap.put(key, value);
}
return computeScore(coeffMap, varMap);
} catch (IllegalArgumentException e) {
System.err.println(e.getMessage());
return 0;
}
}
}
public static double computeScore(MultiKeyCoefficientMap amap, Map<String, Double> values)
{
//Do some stuff
}
非常感谢您花时间通读此代码。 如果您知道我做错了什么,请告诉我!
非常感谢和最良好的祝愿,
R
您拥有的关键错误假设是IDoubleSource.Variables
枚举以某种方式连接到Person.Variables
枚举。 它们完全无关。 (它们恰好具有相同的简单名称。
当一个类(如Person
)实现一个接口(如IDoubleSource
)时,该类声明它将在该接口中提供(非default
)方法的实现。 已实现接口中的任何内部类、内部枚举或内部接口仅在它们出现在必须实现的接口方法之一的签名中时才相关。
因此,您可以将界面更改为:
public interface IDoubleSource {
public enum Variables {
Default;
}
public double getDoubleValue(Variables variableID);
}
。但是,传递给任何getDoubleValue
实现的唯一合法值是Default
- IDoubleSource
的实现者不能扩展允许的枚举值集。
我认为你真正想做的是声明IDoubleSource
的实现者必须声明他们处理的枚举类型:
public interface IDoubleSource<T extends Variables & Enum<T>> {
public interface Variables { }
public double getDoubleValue(T variableID);
}
您在这里所说的是,getDoubleValue()
方法的实现者必须使用某个枚举类型作为其参数,并且该类型还必须实现Variables
接口。 (如果没有有意义的方法可以放入该内部接口,则可以为简单起见将其删除。
然后你的实现将如下所示:
public class Person implements IDoubleSource<PersonVariables> {
public enum PersonVariables implements Variables {
nChildren,
durationInCouple,
ageDiff;
}
public double getDoubleValue(PersonVariables variableID) {
switch (variableID) { //no cast necessary here!
case nChildren:
// ...
default:
// this is now really impossible
// if the rest of your program has no unsafe casts
throw new IllegalArgumentException("Unsupported variable");
}
}
}
然后,最后一个技巧是增强computeScore
方法的签名,以确保iDblSrc
参数使用与映射中找到的枚举类型相同的枚举类型:
public static <T extends IDoubleSource.Variable & Enum<T>>
double computeScore(MultiKeyCoefficientMap<T,?> coeffMap,
IDoubleSource<T> iDblSrc);
然后映射中的键根本不会String
,而是正确枚举类型的实例。
这里有多个问题:
- 在由另一个类实现(扩展)的接口(或类)中声明的枚举不会被实现类覆盖。 所以你上面有两个完全不同的枚举,它们恰好具有相同的本地名称。 但一个是IDoubleSource.Variables,有一个值:IDoubleSource.Variables.Default,另一个是Person.Variables,有三个值,其中一个是Person.Variables.nChildren
- 正如 OP 指出的那样,您不能简单地将 String(可能具有与某个枚举的名称匹配的值)强制转换为枚举,并将其解析为预期的枚举值。
鉴于这两件事,并且您似乎想为子类型特定类型的东西选择不同的处理,那么在最坏的情况下,您可以将字符串键作为参数传递,然后在内部改变逻辑。 但实际上,您已经提出了一个方案,您需要了解子类型才能请求适当的(支持的)处理。 这不允许使用接口/实现类时预期的解耦类型。 您可能需要在此处查看目标并制定出更好的设计。