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