Лучший способ передать аргументы лямбде в хэш-карте в Java

#java #lambda

#java #лямбда

Вопрос:

У меня есть CLI, который принимает разные команды. Чтобы избежать чрезмерно длинного оператора switch, я решил поместить свои команды в хэш-карту и иметь единственную функцию, которая проверяет, существует ли команда, а затем вызывает ее, если она существует.

 public class Commands {
  private LinkedHashMap<String, Runnable> commands = new LinkedHashMap<String, Runnable>();

  public Commands(Scanner userInput) {
    commands.put("foo", () -> this.foo());
    commands.put("bar", () -> this.bar());
    commands.put("foobar", () -> this.foobar());
    ... and so on 
  }

  private void handleCommand(String command) {
    if (!commands.containsKey(command)) {
      System.out.printf("'%s' is an invalid command%n", command);
      return;
    }

    commands.get(command).run();
  }

  public void foo() { ... }
  public void bar() { ... }
  public void foobar() { ... }
}
  

Это сотворило чудеса, и я смог быстро добавить множество команд.

Теперь у меня проблема, когда некоторые из моих команд требуют аргументов. например $ foo arg1 arg2 , и я хочу сохранить структуру, которая у меня есть, так как я изначально написал ее таким образом, потому что я думал, что ее будет легко расширить.

Я изменил свой метод handleCommand() следующим образом:

   private void handleCommand(String command) {
    // Split the command to get possible args
    String[] args = command.split(" ");

    if (!commands.containsKey(args[0])) { // the first string is the command we want
      System.out.printf("'%s' is an invalid command%n", args[0]);
      return;
    }

    commands.get(args[0]).run(Arrays.copyOfRange(args, 1, args.length));
  }
  

и изменение соответствующих методов следующим образом

     ...
    commands.put("foo", (args) -> this.foo(args));
    ...

    public void foo(String[] args) { ... };
  

Я также пытался использовать интерфейс, в котором я переопределяю run , я пытался использовать вызываемый вместо выполняемого. Я просто в недоумении, что попробовать сейчас. Многие примеры, которые я нашел, намного сложнее, чем то, что мне нужно, и я изо всех сил пытаюсь следовать им.

Я исхожу из опыта javascript, где это прекрасно делать:

 obj = { foo: (args) => { ...do something with args } }

obj.foo(args)
  

Итак, это мое мышление, почему я хочу сделать это таким образом.

TIA

Комментарии:

1. В вашем тексте даже нет вопросительного знака, так в чем вопрос?

2. Как мне передать аргументы лямбде, хранящейся в хэш-карте?

3. Начиная с Java 8, вы можете заменить Runnable , Consumer например, на a.

Ответ №1:

Вы можете использовать Consumer вместо Runnable . Consumer принимает параметр и ничего не возвращает. Если вы хотите также вернуть параметр, который вам нужно использовать Function . Итак, ваш код будет выглядеть следующим образом:

 private LinkedHashMap<String, Consumer<String[]>> commands = new LinkedHashMap<>();

public Commands(Scanner userInput) {
    commands.put("foo", this::foo); // with method reference. Its the same as next line
    commands.put("bar", input -> this.bar(input));
    commands.put("foobar", input -> this.foobar(input));
}

....
commands.get(command).accept(args); // Consumer's methods is accept
  

Вы можете проверить предопределенные функциональные интерфейсы здесь.

Ответ №2:

В дополнение к использованию Consumer<String[]> , вы также можете определить пользовательский интерфейс для реализации ваших команд:

 public static interface Command {
    public void run(String ... args);
}
  

Использовать точно так, как вы ожидаете:

 private LinkedHashMap<String, Command> commands = new LinkedHashMap<>();

commands.put("foo", (input) -> foo(input));
  

Не бойтесь объявлять свои собственные специфические интерфейсы.

Комментарии:

1. Аааа! Спасибо! Я действительно попробовал это, следуя чему-то еще, что я нашел, но не смог понять, как объявить саму лямбду как разновидность этого интерфейса! Конечно, имеет смысл объявить, что в хэш-карте я глупый!