1

I am have a general question about mapping things in java. I am not quite stuck, but I feel that my approach is not really beautiful and could cause problems if one would imagine my small private project will become bigger.

Imagine I have an array = {1, 2, 3, 4}, it does not have to be numbers, just any sign in general. Then I get an input from a user, which can only be part of an array (no invalid inputs possible). Now depends on which element they choose, a specific function will be executed. The only thing I came up with is:

switch (input) {
   case array[0]:
      doOneThing();
      break;
   case array[1]:
      doAnotherThing();
     break;
   case array[2]:
      doAThirdThing();
      break;
   case array[3]:
      doSomethingElse();
       break;
}

This method works, but is not beautiful to expand. I take every possible idea with great thanks.

grafpatron
  • 514
  • 1
  • 3
  • 20
  • Did you try and compile that construct? Case targets must be constants and array elements don't match that requirement. – WJS Sep 04 '20 at 22:00
  • Yeah I got that error. So my approach doesn't even work. Do you know how to make a constant array? – grafpatron Sep 05 '20 at 10:02

3 Answers3

1

You must have constants in your switch statement so what you specified won't work. You could do something like this though as an example. You're not limited to using Runnable lambdas. Any functional Interface could be used and the appropriate arguments supplied when called.

Note that it is not a good idea to allow any method of any class to be invoked as it could lead to unexpected results or security issues depending on how it is to be used.

public class ThisClass {
    
    public static void main(String[] args) {
        Map<String, Runnable> map = new HashMap<>();
        ThisClass tc = new ThisClass();
        
        map.put("A", () -> tc.doOneThing());
        map.put("B", () -> tc.doAnotherThing());
        map.put("C", () -> tc.doAthirdThing());
        map.put("D", () -> tc.doSomethingElse());
        
        for (String str : new String[] { "A", "B", "Q", "C", "D", "E" }) {
            map.getOrDefault(str, () -> tc.defaultMethod()).run();
        }
    }
    
    public void doOneThing() {
        System.out.println("Doing one thing");
    }
    
    public void defaultMethod() {
        System.out.println("Executing default");
    }
    
    public void doAnotherThing() {
        System.out.println("Doing Another thing");
    }
    
    public void doAthirdThing() {
        System.out.println("Doing a third thing");
    }
    
    public void doSomethingElse() {
        System.out.println("Doing something Else");
    }
}

Prints

Doing one thing
Doing Another thing
Executing default
Doing a third thing
Doing something Else
Executing default

You can also do something like this.

Map<String, DoubleBinaryOperator> map = new HashMap<>();

map.put("+", (a, b) -> a + b);
map.put("-", (a, b) -> a - b);
map.put("*", (a, b) -> a * b);
map.put("/", (a, b) -> a / b);

DoubleBinaryOperator error = (a, b) -> {
    throw new IllegalArgumentException();
};

for (String str : new String[] { "+", "-", "L", "/", "*" }) {
    try {
        double result = map.getOrDefault(str, error)
                .applyAsDouble(2.0, 3.0);
        System.out.println("result = " + result);
    } catch (IllegalArgumentException iae) {
        System.out.println("unknown operator \"" + str + "\"");
    }
}

Prints

result = 5.0
result = -1.0
unknown operator "L"
result = 0.6666666666666666
result = 6.0

WJS
  • 22,083
  • 3
  • 14
  • 32
  • BruchGUI.java:12:18: error: expected map.put("+", () -> obj1.foo(obj2)); ^ obj1 and obj2 are objects of another class – grafpatron Sep 05 '20 at 10:14
0

One possible way is if you can create a name of a function using their inputted value. For example, if they input 1, you will call "function1" or if you input 2, you call "function2". Then you can use reflection to call the method by it's string name.

Something like this.

fooiey
  • 549
  • 1
  • 12
0

Sounds like you want to look at Java Reflection Api.

Class c=Class.forName("xxxx")

where {xxxx} is a string you have constructed from parameters.

you can similarly invoke method names.

Note - beautiful code often comes at some cost - be it readability, performance or security (esp in the case of reflection). Personally i don't mind a long switch statement - it might not be pretty but it will never bite you.

batman567
  • 642
  • 1
  • 5
  • 17