简体   繁体   中英

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?

I'd be extremely grateful if anyone could point out what I'm doing wrong.

I have an interface IDoubleSource, which I implement in a Person class. There is a LinearRegression class with a method that takes an IDoubleSource argument, but I will pass in the Person class.

As part of the IDoubleSource interface, an enum called Variables and a method called getDoubleValue(Enum) must be defined. Below, I show how I have done this in Person, and that the enum types are used to specify switch cases in the getDoubleValue() method.

The problems:

1) In LinearRegression, there is a method computeScore((MultiKeyCoefficient)Map, IDoubleSource), where the last argument is an interface. I cannot seem to access the Variables enum of the instance of the implementation of IDoubleSource within the computeScore method, despite having the interface imported into the LinearRegression class. It just doesn't register that an IDoubleSource has an enum called Variables (though I can call the getDoubleValue() method fine). Is there anything I'm obviously doing wrong, that prevents me accessing the enum Variables?

2) The getDoubleValue(Enum) method in Person class is designed to return a double value that depends on the value of the enum Variable passed to it. By looping through the keys (which are of String type) of a (MultiKeyCoefficient)Map in the LinearRegression class, I would like to use the keys to specify the enum values that I want as an argument to getDoubleValue(Enum) in the LinearRegression class (I would like getDoubleValue() to return several different values based on the Enum values it receives in the loop). However, I cannot use the (String) key in place of the expected enum as I get a ClassCastException java.lang.String cannot be cast to java.lang.Enum. How can I use the keys of the map to specify the Enums?

I'm not very familiar with using Enum types in Java, which may be a large part of my problem.

Now the code details:

I implement the following interface:

IDOUBLESOURCE INTERFACE

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);

}

by creating the class:

PERSON CLASS

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");
    }

In another package, I have a Class:

LINEARREGRESSION CLASS

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  
}

I'm very grateful that you've taken the time to read through this code. Please do let me know if you have any idea what I'm doing wrong!

Many Thanks and Best Wishes,

R

The key incorrect assumption you have is that the IDoubleSource.Variables enum is connected in some way to the Person.Variables enum. They're totally unrelated. (They just happen to have the same simple name.)

When a class (like Person ) implements an interface (like IDoubleSource ), that class is declaring that it will provide implementations of the (non- default ) methods in that interface. Any inner classes, inner enums, or inner interfaces within the implemented interface are only relevant if they appear in the signatures of one of the interface methods that must be implemented.

So you could change your interface to:

public interface IDoubleSource {
    public enum Variables {
        Default;
    }

    public double getDoubleValue(Variables variableID);
}

... but then the only legal value to pass in to any implementation of getDoubleValue is Default -- implementors of IDoubleSource can't extend the set of allowed enum values.

I think what you really want to do is to declare that implementors of IDoubleSource must declare what type of enum they deal in:

public interface IDoubleSource<T extends Variables & Enum<T>> {
    public interface Variables { }

    public double getDoubleValue(T variableID);
}

What you're saying here is that an implementor of the getDoubleValue() method must use some enum type as its arg, and that type must also implement the Variables interface. (If there are no meaningful methods to put in that inner inteface, you can drop it for simplicity.)

Then your implementation would look like this:

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");
        }
    }
}

The last trick, then, is to enhance the signature of your computeScore method to ensure that the iDblSrc argument uses the same enum type as those found in the map:

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

Then the keys in the map won't be String s at all, but rather instances of the right enum type.

There are multiple problems here:

  1. An enum declared in an interface (or class) implemented (extended) by another class is NOT overridden by the implementing class. So what you have above is two completely different enums, which happen to have the same local name. But one is IDoubleSource.Variables, with one value: IDoubleSource.Variables.Default, and the other is Person.Variables, with three values, one of which is Person.Variables.nChildren
  2. As the OP pointed out, you cannot simply cast a String (which presumably has a value matching the name of some enum) to an enum, and have it resolve to the expected enum value.

Given these two things, and that it seems you want to select different processing for subtype specific types of things, then at worst, you could pass the string key as an argument, and then vary the logic internally. But really, you have come up with a scheme where you need to have knowledge of the subtype in order to request appropriate (supported) processing. This does not allow for the type of decoupling that is intended when using an interface/implementing class(es). You may want to review the objectives here and work out a better design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM