Frage

For later reference:

Operations o = new Operations(); //class containing the operation methods
HashMap<String, Method> ops = new HashMap<String, Method>();

I'm working on a program that will parse a mathematical expression input via console or eventually maybe a GUI.

Currently, I have a class called "Operations" which has various basic math functions (more will be added later, just testing right now). In another class, I have a method which calculates the result by taking an operand, the successive operator, and another operand, and invoking a method to evaluate the expression. It stores the info necessary to the calculation in:

double numOne = //...
char operator = //...
double numTwo = //...
double result = //...

Now I don't want to have a long switch/case statement or if statement saying:

if (operator.equals("+")) //I know .equals() doesn't use chars; it's an example
     o.add(numOne, numTwo);
else if (operator.equals("-"))
     o.subtract(numOne, numTwo);

on and on for every operation. That's why I tried creating a HashMap<String, Method> to store the operator (String) and the method which should be called. Basically, in the current class' constructor, I put:

ops.put("+", o.getClass().getMethod("add", double.class, double.class));
ops.put("-", o.getClass().getMethod("subtract", double.class, double.class));
//etc. Which in and of itself is also pretty lengthy

Now to once the method is identified via operator, I need another method to return a Method to call.

private Method getMethodFromKey(HashMap<String, Method> map, char op) {
    Method method = null;
    for (Map.Entry<String, Method> e: map.entrySet()) {
        if (e.getKey().equals(op))
            method = e.getValue();
    }
    return method;
}

Finally, once I have the correct method, I can call it with:

getMethodFromKey(ops, operator).invoke(o, numOne, numTwo);

That all works great. My problem is, the method(s) I am/will be invoking are getter methods; they return a double. Example:

public double add(double one, double two) {
    double answer = 0;
    answer = one + two;
    return answer;
}

I guess this is all just a long-winded way of asking is there a way to assign the returned value of an innvoked method? Something like:

result = getMethodFromKey(ops, operator).invoke(o, numOne, numTwo); //This doesn't work :(

Any help is appreciated. Additionally, if my aproach is completely wrong, I would appreciate a bump in the right direction.


DISCLAIMER: I'm relatively inexperienced at Java and known for overcomplicating things, so please point out any grievous flaws in my design. :)

War es hilfreich?

Lösung

invoke() returns Object and since Java does not know how to assign an Object to a double this won't compile. invoke starts by boxing the double from the method into a Double. You have to cast it now from Object to Double (and could then call .doubleValue() but that's done automatically) for that to work.

I'm [...] known for overcomplicating things, so please point out any grievous flaws in my design. :)

Using reflection instead of an interface. A Method is a function object. But it's not type-safe to use. An interface can do the same without those problems.

interface Operation {
    double evaluate(double a, double b);
}

Then put objects that implement the interface in your map:

ops.put("+", new Operation() {
    public double evaluate(double a, double b) {
        return a+b;
    });

and you can do

double result = getMethodFromKey(ops, operator).evaluate(numOne, numTwo);

The need to cast is gone.

Andere Tipps

If you're sure that all of your operations are going to be on a single class (no extensibility), then you should consider using an enum instead. You can add an instance field to the enum to represent the character command corresponding to the operation and then have an abstract evaluate method that's implemented by each enum value.

If invoke() is returning an Object that you know is a double, you can cast it like so:

result = (Double) getMethodFromKey(ops, operator).invoke(o, numOne, numTwo); 

Since double is a primitive, which is not of type Object, you need to cast it to a Double, and through unboxing, we get a double.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top