2

I have a list and I have functions.

I want to iterate the list and call the function that matches the lists element. How can I do that in a more elegant way than comparing the name in a if or switch?

class C
{
  String [] lst = {"foo", "bar", "zoo"};

  void foo () { /* ... */ }
  void bar () { /* ... */ }

  void NotSoElegant ()
  {
    for (String s : lst)
    {
      if (s.equals ("foo") == true)
      { foo (); }
      if (s.equals ("bar") == true)
      { bar (); }
    }
  }
}
3
  • 2
    You can have a look at this (Possible duplicate): How do I invoke a Java method when given the method name as a string? Commented Jan 27, 2021 at 9:13
  • 1
    You could create a Map<String, Runnable> or Map<String, Consumer<C>> to avoid your if branches. Commented Jan 27, 2021 at 9:22
  • @maloomeister For a case like this, that answer was made obsolete by Java 8. Commented Jan 27, 2021 at 9:34

2 Answers 2

5

I strongly advise against to use reflection in such case - it's obviously code smell, it's breaching the security of your code and may cause many, many issues and drawbacks - just think what will happen if someone will put NotSoElegant in the list.

Of course there are methods to fetch method by name so basically you could scan the class, get method by string, handle exceptions... Do not do this.

Basically there is nothing bad in having such set of ifs however you can refactor this a little bit using switch statement

for (String s : lst) {
    switch(s) {
        case "foo": 
            foo();
            break;
        case "bar": 
            bar();
            break;
    }
}

If you want to be more SOLID complain and do not break Open-Closed rule you should rather think about using some design patterns (like Strategy) but do not use reflection :)

Sign up to request clarification or add additional context in comments.

1 Comment

Arrow-cases would make the code even better to read.
3

Use the Replace Conditional with Polymorphism refactoring along with lambdas/method references. Assuming that your methods don't take parameters and don't return anything, they match the Runnable interface:

class MoreElegant {

  static final Map<String, Runnable> COMMANDS = Map.of(
    "foo", MoreElegant::foo,
    "bar", MoreElegant::bar,
    "baz", () -> { doSomethingElse(); }
  );

  void execute(List<String> commands) {
    commands.forEach(c ->
      COMMANDS
        .getOrDefault(c, () -> throw new IllegalArgumentException("no command " + c))
        .run()
    );
  }
}

2 Comments

"Assuming that your methods don't take parameters and don't return anything" — and are static, since you're putting them in a static field.
@khelwood The approach generalizes simply enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.