关于java:如何访问另一个类的枚举类型? 有没有办法使用String(映射键)作为期望Enum <?>类型的方法的参数?

How to access enum type of another class? Is there a way to use a String (a map key) as an argument of a method expecting Enum type?

如果有人能指出我做错了什么,我将非常感激。

我有一个接口IDoubleSource,我在Person类中实现。有一个LinearRegression类,其方法采用IDoubleSource参数,但我将传入Person类。

作为IDoubleSource接口的一部分,必须定义一个名为Variables的枚举和一个名为getDoubleValue(Enum)的方法。下面,我将展示我是如何在Person中完成此操作的,并且枚举类型用于在getDoubleValue()方法中指定switch case。

问题:

1)在LinearRegression中,有一个方法computeScore((MultiKeyCoefficient)Map,IDoubleSource),其中最后一个参数是一个接口。尽管将接口导入LinearRegression类,我似乎无法访问computeScore方法中IDoubleSource实现实例的变量枚举。它只是没有注册IDoubleSource有一个名为Variables的枚举(虽然我可以调用getDoubleValue()方法)。有什么我明显做错了,这阻止我访问枚举变量?

2)Person类中的getDoubleValue(Enum)方法旨在返回一个double值,该值取决于传递给它的枚举变量的值。通过循环遍历LinearRegression类中(MultiKeyCoefficient)Map的键(String类型),我想使用键来指定我想要的枚举值作为LinearRegression类中getDoubleValue(Enum)的参数(我希望getDoubleValue()根据它在循环中收到的Enum值返回几个不同的值)。但是,我不能使用(String)键代替预期的枚举,因为我得到一个ClassCastException java.lang.String无法强制转换为java.lang.Enum。如何使用地图的键来指定枚举?

我不太熟悉在Java中使用Enum类型,这可能是我的问题的很大一部分。

现在代码详细信息:

我实现了以下界面:

IDOUBLESOURCE接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);

}

通过创建类:

人类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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");
    }

在另一个包中,我有一个类:

LINEARREGRESSION CLASS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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,有一个值:IDoubleSource.Variables.Default,另一个是Person.Variables,有三个值,其中一个是Person.Variables.nChildren
  • 正如OP指出的那样,你不能简单地将一个String(可能具有与某个枚举的名称相匹配的值)转换为枚举,并让它解析为预期的枚举值。
  • 鉴于这两件事,并且您似乎想要为子类型特定类型的事物选择不同的处理,那么在最坏的情况下,您可以将字符串键作为参数传递,然后在内部改变逻辑。但实际上,您已经提出了一个方案,您需要了解子类型才能请求适当的(支持的)处理。这不允许在使用接口/实现类时使用的解耦类型。您可能希望在此处查看目标并制定出更好的设计。


    您所拥有的关键错误假设是IDoubleSource.Variables枚举以某种方式连接到Person.Variables枚举。他们完全没有关系。 (它们碰巧有相同的简单名称。)

    当类(如Person)实现接口(如IDoubleSource)时,该类声明它将在该接口中提供(非default)方法的实现。实现的接口中的任何内部类,内部枚举或内部接口仅在它们出现在必须实现的某个接口方法的签名中时才相关。

    所以你可以改变你的界面:

    1
    2
    3
    4
    5
    6
    7
    public interface IDoubleSource {
        public enum Variables {
            Default;
        }

        public double getDoubleValue(Variables variableID);
    }

    ...但是传递给getDoubleValue的任何实现的唯一合法值是default - IDoubleSource的实现者不能扩展允许的枚举值集。

    我认为你真正想做的是声明IDoubleSource的实现者必须声明他们处理的枚举类型:

    1
    2
    3
    4
    5
    public interface IDoubleSource<T extends Variables & Enum< T >> {
        public interface Variables { }

        public double getDoubleValue(T variableID);
    }

    你在这里说的是getDoubleValue()方法的实现者必须使用一些枚举类型作为其arg,并且该类型还必须实现Variables接口。 (如果没有有意义的方法放入内部接口,可以放弃它以简化。)

    然后你的实现看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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参数使用与地图中相同的枚举类型:

    1
    2
    3
    public static <T extends IDoubleSource.Variable & Enum< T >>
    double computeScore(MultiKeyCoefficientMap<T,?> coeffMap,
                        IDoubleSource< T > iDblSrc);

    然后地图中的键根本不是String,而是右枚举类型的实例。