35

Right now I'm going to have to write a method that looks like this:

public String Calculate(String operator, double operand1, double operand2)
{

        if (operator.equals("+"))
        {
            return String.valueOf(operand1 + operand2);
        }
        else if (operator.equals("-"))
        {
            return String.valueOf(operand1 - operand2);
        }
        else if (operator.equals("*"))
        {
            return String.valueOf(operand1 * operand2);
        }
        else
        {
            return "error...";
        }
}

It would be nice if I could write the code more like this:

public String Calculate(String Operator, Double Operand1, Double Operand2)
{
       return String.valueOf(Operand1 Operator Operand2);
}

So Operator would replace the Arithmetic Operators (+, -, *, /...)

Does anyone know if something like this is possible in java?

James T
  • 1,079
  • 3
  • 13
  • 17

9 Answers9

49

No, you can't do that in Java. The compiler needs to know what your operator is doing. What you could do instead is an enum:

public enum Operator
{
    ADDITION("+") {
        @Override public double apply(double x1, double x2) {
            return x1 + x2;
        }
    },
    SUBTRACTION("-") {
        @Override public double apply(double x1, double x2) {
            return x1 - x2;
        }
    };
    // You'd include other operators too...

    private final String text;

    private Operator(String text) {
        this.text = text;
    }

    // Yes, enums *can* have abstract methods. This code compiles...
    public abstract double apply(double x1, double x2);

    @Override public String toString() {
        return text;
    }
}

You can then write a method like this:

public String calculate(Operator op, double x1, double x2)
{
    return String.valueOf(op.apply(x1, x2));
}

And call it like this:

String foo = calculate(Operator.ADDITION, 3.5, 2);
// Or just
String bar = String.valueOf(Operator.ADDITION.apply(3.5, 2));
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Enums cannot have abstract methods. – Ashish Jindal May 25 '10 at 06:28
  • +1, but typo in subtraction implementation and identifier of second argument. – aioobe May 25 '10 at 06:29
  • 3
    @aioobe: Yup, I'd just got to the identifier but had missed the implementation. @Ashsish: Yes they can, if all the values override it. – Jon Skeet May 25 '10 at 06:31
  • This solves my problem! Thanks for sharing your solution. In a dynamic programming language like Python, there would be an eval() method to rescue, but there is none in Java. – Kenny Meyer Sep 13 '10 at 17:14
  • 1
    @KennyMeyer In Python one wouldn't use evil `eval()` but passing the functions in the `operator` module as arguments. `def calculate(op, a, b): return op(a, b)` and the called as `calculate(operator.add, 3.5, 2)`. – BlackJack Jul 08 '17 at 10:01
10

Method arguments in Java must be expressions. An operator by itself is not an expression. This is not possible in Java.

You can, of course, pass objects (maybe enum constants) that represents those operators, and act accordingly, but you can't pass the operators themselves as parameters.


Additional tips

Since you're just starting Java, it's best to ingrain these informations early on to ease your future development.

  • Method names starts with lowercase: calculate instead of Calculate
  • Variable names starts with lowercase: operator instead of Operator
  • Double is a reference type, the box for primitive type double.
    • Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives
  • Don't return "error...". Instead, throw new IllegalArgumentException("Invalid operator");

See also

Community
  • 1
  • 1
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
6

There's only the cumbersome way of doing it with a callback interface. Something like

interface Operator {
    public Double do(Double x, Double y);
}

Then you implement the operators you need:

Operator plus = new Operator() {
    public Double do(Double x, Double y) {
        return x + y;
    }
};

And your generic method takes an Operator and the two arguments:

public String Calculate(Operator operator, Double x, Double y) {
    return String.valueOf( operator.do(x, y) );
}

You could also use an enum instead of an interface if you only need a smaller, fixed number of operators.

Thomas Kappler
  • 3,795
  • 1
  • 22
  • 21
  • 1
    Cumbersome perhaps, but this is considerably more flexible and extensible than using an enum. – Daniel Pryden May 25 '10 at 06:32
  • 2
    That it is. Especially since you can use anonymous classes for one-off operators. – Thomas Kappler May 25 '10 at 06:45
  • 1
    @Daniel: Yes, it depends on whether you have a fixed set of operators to start with. If you do, an enum is neater IMO (and allows for serialization etc). If you need the extra flexibility, the anonymous inner class bit works fine. Of course, you could always make an enum which implements the interface, to get the best of both worlds :) – Jon Skeet May 25 '10 at 06:59
  • @Jon: True enough. That's why I didn't say that this approach is necessarily *better*, just that it "is considerably more flexible and extensible." In many cases, "flexible and extensible" equals better, but obviously not always. (Serialization is an excellent counterexample, BTW.) It just goes to show you should never pick your design until you know the constraints of the problem! – Daniel Pryden May 25 '10 at 07:07
4

You can't pass operators directly. You could use functors.

public double Calculate(BinaryFunction<Double, Double, Double> op, double Operand1, double Operand2)
{
  return (double)op.evaluate(Operand1, Operand2);
}
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
2

You can either

  1. Use a functional language for JVM to implement this part of your code (clojure, scala et el), wrap lambda functions around math operators and pass those functions as parameters

  2. Get an expression evaluator for Java like http://www.singularsys.com/jep/ (and there must be many free alternatives as well)

Midhat
  • 17,454
  • 22
  • 87
  • 114
1

No this is not possible in this way.

You will need a parser to do what you want, and this can be cumbersome.

You're probably asking the wrong question, since you are getting the wrong answer.

If you are looking for a mathematical parser you might want to take a look at this project on SF: http://sourceforge.net/projects/jep/

There might be some answers in this.

Anemoia
  • 7,928
  • 7
  • 46
  • 71
1

It would be nice, wouldn't it? But, you just can't do that. You can probably accomplish, something similar by writing your own "operators".

public interface Operator {
  Double calculate(Double op1, Double op2);
}

public Addition implements Operator {
  @Override
  Double calculate(Double op1, Double op2) { return op1 + op2; }
}

public class Calculator {
  private static Operator ADDITION = new Addition();
  private static Map<String,Operator> OPERATORS = new HashMap<String,Operator>();
  static {
    OPERATORS.put("+",ADDITION);
  }

  public String Calculate(String operator, Double operand1, Double operand2) {
    return String.valueOf(OPERATORS.get(operator).calculate(operand1,operand2);
  }
}

You get the picture how to extend this to many other operators ... and not only Doubles obviously. The advantage of my method is that you can actualy keep your method signature of accepting a String operator.

Strelok
  • 50,229
  • 9
  • 102
  • 115
0

Since the introduction of lambda expressions and functional interfaces in Java 8, you can do it more idiomatically.

public static <T> T calculate(BinaryOperator<T> operator, T operand1, T operand2) {
  return operator.apply(operand1, operand2);
}

Whether you want to have these BinaryOperators predefined (or declared somewhere as constants) is more of a stylistic choice.

Integer r1 = calculate((a, b) -> a + b, 2, 2); // 4
Integer r2 = calculate(Integer::sum, 2, 2); // 4
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
-3

Operators, AFAIK, cannot be passed as a parameter in any language (at least that I've come across).

Reason is that only values (either by copy or by references) can be passed as "values to parameters".

And operators represent no values.

  • 3
    Operators can be passed as parameters in every functional language I know. Scala can do it on the JVM. – Daniel Pryden May 25 '10 at 06:30
  • 3
    Yeah, I died a little inside when I read that part. The ability to pass operators is one of those things Java-oriented people never consider, and then they find a language that supports it and hopefully it blows their minds – Michael Mrozek May 25 '10 at 06:31
  • @Daniel: That tells me to look into Scala :) In anycase, the operator stuff doesn't apply to Java ;) – Ashish Jindal May 25 '10 at 06:37