1

I'm doing an exercise where I prompt the user to give me their operator of choice and two different numbers and then I output the result. I know I can easily do this exercise with a if else or a switch case however I'd really like to make it work this way. The problem is at my operator method, apparently in Java you can't pass in a parameter and use that parameter to call a method. Is there a way to make this work? If you were confused by the question I'd be glad to clarify.

import java.util.Scanner;

public class methods {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Welcome, choose your operator, would you like to add, subtract, multiply, or divide?");
        String typeofOperator = sc.nextLine();
        System.out.print("Enter the first number: ");
        int firstNumber = sc.nextInt();
        System.out.print("Enter the second number: ");
        int secondNumber = sc.nextInt();
        operator(typeofOperator, firstNumber, secondNumber);
    }

    public static void operator(String typeofOperator, int firstNumber, int secondNumber) {
        typeofOperator(firstNumber, secondNumber);
    }

    public static int add(int firstNumber, int secondNumber) {
        int answer = firstNumber + secondNumber;
        result(answer);
    }
    public static int subtract(int firstNumber, int secondNumber) {
        int answer = firstNumber - secondNumber;
        result(answer);
    }
    public static int multiply(int firstNumber, int secondNumber) {
        int answer = firstNumber * secondNumber;
        result(answer);
    }
    public static int divide(int firstNumber, int secondNumber) {
        int answer = firstNumber / secondNumber;
        result(answer);
    }
    public static void result(int result) {
        System.out.println("This is the result: " + result);
    }
}
  • 1
    Not related, but I think that the fact that subtract, multiply and divide are all implemented using `+` is probably a mistake... – Nir Alfasi Jan 05 '17 at 21:10
  • This can help you http://stackoverflow.com/questions/160970/how-do-i-invoke-a-java-method-when-given-the-method-name-as-a-string – Matteo Gaggiano Jan 05 '17 at 21:14

6 Answers6

5

Use map with functions. It will be in Java 8 style:

public class methods {

    private static Map<String, BiFunction<Integer, Integer, Integer>> OPERATOR_MAP = new HashMap<>();
    static {
        OPERATOR_MAP.put("add", methods::add);
        OPERATOR_MAP.put("subtract", methods::subtract);
        OPERATOR_MAP.put("multiply", methods::multiply);
        OPERATOR_MAP.put("divide", methods::divide);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Welcome, choose your operator, would you like to add, subtract, multiply, or divide?");
        String typeofOperator = sc.nextLine();
        System.out.print("Enter the first number: ");
        int firstNumber = sc.nextInt();
        System.out.print("Enter the second number: ");
        int secondNumber = sc.nextInt();
        operator(typeofOperator, firstNumber, secondNumber);
    }

    public static void operator(String typeofOperator, int firstNumber, int secondNumber) {
        OPERATOR_MAP.get(typeofOperator).apply(firstNumber, secondNumber);
    }

    public static int add(int firstNumber, int secondNumber) {
        int answer = firstNumber + secondNumber;
        result(answer);
        return 0;
    }
    public static int subtract(int firstNumber, int secondNumber) {
        int answer = firstNumber - secondNumber;
        result(answer);
        return 0;
    }
    public static int multiply(int firstNumber, int secondNumber) {
        int answer = firstNumber * secondNumber;
        result(answer);
        return 0;
    }
    public static int divide(int firstNumber, int secondNumber) {
        int answer = firstNumber / secondNumber;
        result(answer);
        return 0;
    }
    public static void result(int result) {
        System.out.println("This is the result: " + result);
    }
}

Of course there is no checking that user can specify non existent operator.

UPDATE: added "return 0" to all operator methods to make code compilable. I just wanted to make minimal changes to the original code to specify way how to reach required functionality.

UPDATE2: If operator methods are not required then I'd like to write it simpler:

public class methods {

    private static Map<String, BiFunction<Integer, Integer, Integer>> OPERATOR_MAP = new HashMap<>();
    static {
        OPERATOR_MAP.put("add", (x, y) -> x + y);
        OPERATOR_MAP.put("subtract", (x, y) -> x - y);
        OPERATOR_MAP.put("multiply", (x, y) -> x * y);
        OPERATOR_MAP.put("divide", (x, y) -> x / y);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Welcome, choose your operator, would you like to add, subtract, multiply, or divide?");
        String typeofOperator = sc.nextLine();
        System.out.print("Enter the first number: ");
        int firstNumber = sc.nextInt();
        System.out.print("Enter the second number: ");
        int secondNumber = sc.nextInt();
        System.out.println(OPERATOR_MAP.get(typeofOperator).apply(firstNumber, secondNumber));
    }
}
Dmitry Gorkovets
  • 1,898
  • 8
  • 18
2

in Java you can't pass in a parameter and use that parameter to call a method

Well, you can start doing if/else and figure out which method should be called based on the operator-type. That said, it feels like an object-oriented approach would be cleaner:

We can define an Operator interface:

interface Operator {
    int call(int first, int second);
}

and then create classes that implement it: one per each operation:

class Add implements Operator {
    @Override
    public int call(int first, int second) {
        return first + second;
    }
}

class Subtract implements Operator {
    @Override
    public int call(int first, int second) {
        return first - second;
    }
}

class Multiply implements Operator {
    @Override
    public int call(int first, int second) {
        return first * second;
    }
}

class Divide implements Operator {
    @Override
    public int call(int first, int second) {
        return first / second;
    }
}

Now we need a factory that will receive the operator-type as an argument and which will create the related operator object respectively. One way to do it would be:

class OperatorFactory {
    static Operator build(String type) {
        Operator res;
        type = type.trim();
        switch (type) {
            case "add":
                res = new Add();
                break;
            case "subtract":
                res = new Subtract();
                break;
            case "multiply":
                res = new Multiply();
                break;
            case "divide":
                res = new Divide();
                break;
            default:
                throw new IllegalArgumentException(type);
        }
        return res;
    }
}

Let's see how the code works:

public class Methods {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Welcome, choose your operator, would you like to add, subtract, multiply, or divide?");
        String typeofOperator = sc.nextLine();
        System.out.print("Enter the first number: ");
        int firstNumber = sc.nextInt();
        System.out.print("Enter the second number: ");
        int secondNumber = sc.nextInt();
        operator(typeofOperator, firstNumber, secondNumber);
    }

    static void operator(String typeOfOperator, int firstNumber, int secondNumber) {
        Operator operator = OperatorFactory.build(typeOfOperator);
        int result = operator.call(firstNumber, secondNumber);
        System.out.println("result = " + result);
    }
}

class OperatorFactory {
    static Operator build(String type) {
        Operator res;
        type = type.trim();
        switch (type) {
            case "add":
                res = new Add();
                break;
            case "subtract":
                res = new Subtract();
                break;
            case "multiply":
                res = new Multiply();
                break;
            case "divide":
                res = new Divide();
                break;
            default:
                throw new IllegalArgumentException(type);
        }
        return res;
    }
}


interface Operator {
    int call(int first, int second);
}

class Add implements Operator {
    @Override
    public int call(int first, int second) {
        return first + second;
    }
}

class Subtract implements Operator {
    @Override
    public int call(int first, int second) {
        return first - second;
    }
}

class Multiply implements Operator {
    @Override
    public int call(int first, int second) {
        return first * second;
    }
}

class Divide implements Operator {
    @Override
    public int call(int first, int second) {
        return first / second;
    }
}

Two comments:

  • As for "compactness": writing code should be concise indeed, that said, it doesn't mean that the shorter the code is - the better!
    A code should be first and foremost readable as well as maintainable.

  • Since the operator is passed as a string - there is no way to overload a method to do different things based on the type. One answer suggested reflection: while this is the only "compact" way I can think about - it is also a bad choice considering the following:

    1. debuggability
    2. maintainability
    3. code performance (reflection is done at runtime which makes it much slower than its alternatives)
Nir Alfasi
  • 49,889
  • 11
  • 75
  • 119
1

So you call the operator method, but you aren't calling a method from within that. You need to determine what the typeofOperator value is, then you should call the corresponding method associated with that. For example,

public static void operator( String typeofOperator, int firstNumber, int secondNumber){
    if( typeofOperator.equals("+") )
    {
        add(firstNumber, secondNumber);
    }
    else if( typeofOperator.equals("-") )
    {
        subtract(firstNumber, secondNumber);
    }
    else if( typeofOperator.equals("*") )
    {
        multiply(firstNumber, secondNumber);
    }
    else if( typeofOperator.equals("/") )
    {
        divide(firstNumber, secondNumber);
    }
    else
        throw new IllegalArgumentException(); //If we get here, we didn't get a valid argument.
}

As someone else mentioned, you need to change these methods to reflect the type of operation you are implying ( add should add the two numbers, subtract should subtract the two numbers, etc).

Orin
  • 890
  • 5
  • 16
  • I get what you're saying @Orin but for the purposes of making the code as compact as possible I'm trying to see if there is a way to do this without using an if/else or a switch/case. Instead I want to take the user input and use that same input (assuming they typed in their answer correctly) to call a function using the parameters "typeofOperator". – Ruben Varela Jan 05 '17 at 21:23
  • @RubenVarela then you are going to want to use Reflection or method references as Chris Gong suggested below. – Orin Jan 05 '17 at 21:43
0

You can't call Strings as methods. You have to use if or switch-case statements.

switch (typeOfOperator) {

case "add":
add(firstNumber, secondNumber); break;

case "subtract":
subtract(firstNumber, secondNumber); break;

case "multiply":
multiply(firstNumber, secondNumber); break;

case "divide":
divide(firstNumber, secondNumber); break;

}
Grunzwanzling
  • 433
  • 4
  • 14
0

I'd suggest making a HashMap that stores the actual operator as the key and the method name as the value so that you can use it to access a specific method you want to invoke depending on the type of operator inputted by the user. Then, you can use reflection to call a method by a string containing the name of the method.

First, make the HashMap in your main method,

HashMap<String, String> operators = new HashMap<String, String>();
operators.put("+", "add");
operators.put("-", "subtract");
operators.put("*", "multiply");
operators.put("/", "divide");

Then, take the inputted operator to get the correct method name from the hashmap and call the method using reflection in your main method,

String chosenOperator = operators.get(typeOfOperator); //retrieves the method name
methods m = new methods(); //reflection
Method method = m.class.getMethod(chosenOperator);
method.invoke(m, firstNumber, secondNumber); //parameters are the object and
                                             //the method's parameters

EDIT: Instead of using reflection, you could use command patterns to hold the methods as objects and insert them into the HashMap instead of the names of the methods.

This is the Java 8 solution using lambdas,

HashMap<String, Runnable> operators = new HashMap<String, Runnable>();
Runnable addMethod = () -> add(firstNumber, secondNumber);
Runnable subtractMethod = () -> subtract(firstNumber, secondNumber);
Runnable multiplyMethod = () -> multiply(firstNumber, secondNumber);
Runnable divideMethod = () -> divide(firstNumber, secondNumber);
operators.put("+", addMethod);
operators.put("-", subtractMethod);
operators.put("*", multiplyMethod);
operators.put("/", divideMethod);
operators.get(typeOfOperator).run();

By the way, you can do this to make it shorter,

HashMap<String, Runnable> operators = new HashMap<String, Runnable>();
operators.put("+", () -> add(firstNumber, secondNumber));
operators.put("-", () -> subtract(firstNumber, secondNumber));
operators.put("*", () -> multiply(firstNumber, secondNumber));
operators.put("/", () -> divide(firstNumber, secondNumber));
operators.get(typeOfOperator).run();

But I wanted to show that objects of type Runnable were being used.

Chris Gong
  • 7,336
  • 4
  • 26
  • 43
  • 2
    Why reflection? This can be done without. Otherwise it's a good answer. – Jorn Vernee Jan 05 '17 at 21:27
  • @John Vernee perhaps so, but this is just how I would go about without using if/else if statements – Chris Gong Jan 05 '17 at 21:29
  • Yes, you can still use the hashmap, but reflection has to look up the method at runtime. You can wrap the functions in functors, (which is trivial with Java 8 `methods::add`, `methods::multiply` and `IntBinaryOperator`), and add those to the map. That way the methods can be resolved at compile time. – Jorn Vernee Jan 05 '17 at 21:31
  • The problem is that `Runnable` takes 0 arguments, but the wrapped methods take 2. Where are you getting `firstNumber` and `secondNumber` from? – Jorn Vernee Jan 06 '17 at 15:22
  • @JornVernee `firstNumber` and `secondNumber` are the two inputted numbers that OP has in his original code. – Chris Gong Jan 09 '17 at 01:22
0

This can be an alternative solution to if/else and switch methods. Please notice the result() method moved away from every method.

import java.util.Scanner;

public class methods {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Welcome, choose your operator, would you like to add, subtract, multiply, or divide?");
        String typeofOperator = sc.nextLine();
        System.out.print("Enter the first number: ");
        int firstNumber = sc.nextInt();
        System.out.print("Enter the second number: ");
        int secondNumber = sc.nextInt();
        operator(typeofOperator, firstNumber, secondNumber);
    }

    public static void operator(String typeofOperator, int firstNumber, int secondNumber) {
        java.lang.reflect.Method method;
        Object returnedValue = null;
        try {
            method = methods.class.getDeclaredMethod(typeofOperator, int.class, int.class);
            returnedValue = method.invoke(null, firstNumber, secondNumber);
        } catch (Exception e) {
            e.printStackTrace();
        }
        result((Integer) returnedValue);
    }

    public static int add(int firstNumber, int secondNumber) {
        int answer = firstNumber + secondNumber;
        return answer;
    }
    public static int subtract(int firstNumber, int secondNumber) {
        int answer = firstNumber - secondNumber;
        return answer;
    }
    public static int multiply(int firstNumber, int secondNumber) {
        int answer = firstNumber * secondNumber;
        return answer;
    }
    public static int divide(int firstNumber, int secondNumber) {
        int answer = firstNumber / secondNumber;
        return answer;
    }
    public static void result(int result) {
        System.out.println("This is the result: " + result);
    }
}
Matteo Gaggiano
  • 1,120
  • 14
  • 24