英文:
Best way to pass arguments to a lambda in a hashmap in Java
问题
我有一个命令行界面(CLI),它接受不同的命令。为了避免一个冗长的 switch 语句,我决定将我的命令放入哈希映射中,并且有一个单一的函数来检查命令是否存在,如果存在则调用它。
```java
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());
... 以此类推
}
private void handleCommand(String command) {
if (!commands.containsKey(command)) {
System.out.printf("'%s' 是无效的命令%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) {
// 分割命令以获取可能的参数
String[] args = command.split(" ");
if (!commands.containsKey(args[0])) { // 第一个字符串是我们想要的命令
System.out.printf("'%s' 是无效的命令%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
方法,我尝试过使用 Callable 而不是 Runnable。我现在不知道该尝试什么了。我找到的许多示例比我所需的复杂得多,我很难理解它们。
我以前主要从 JavaScript 背景过来,在 JavaScript 中这样做是完全可以的:
obj = { foo: (args) => { ...处理参数args的内容 } }
obj.foo(args)
因此,这就是我想要以这种方式实现的原因。
谢谢您的帮助!
<details>
<summary>英文:</summary>
I have a CLI which accepts different commands. In order to avoid an extensibly long switch statement, I decided to put my commands in a hashmap and have a single function that checks if the command exists and then calls it if it does.
```java
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() { ... }
}
This has worked wonders and I've been able to quickly add lots of commands.
I now have an issue where some of my commands require arguments. eg $ foo arg1 arg2
and I want to keep the structure I have as I originally wrote it this way because I thought it would be easy to extend.
I have changed up my handleCommand() method like so:
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));
}
and modifying the appropriate methods like so
...
commands.put("foo", (args) -> this.foo(args));
...
public void foo(String[] args) { ... };
I have also tried using an Interface where I override run
, I've tried using Callable instead of Runnable. I am just at a loss what to try now. A lot of the examples I've found are much much more complex than what I need and I struggle to follow them.
I am coming from a javascript background where it's perfectly fine to do:
obj = { foo: (args) => { ...do something with args } }
obj.foo(args)
So this is my thinking behind why I want to do it this way.
TIA
答案1
得分: 3
你可以使用Consumer
来替代Runnable
。Consumer
接受一个参数并且不返回任何值。如果你想要返回一个参数,你需要使用Function
。所以你的代码将会是这样的:
private LinkedHashMap<String, Consumer<String[]>> commands = new LinkedHashMap<>();
public Commands(Scanner userInput) {
commands.put("foo", this::foo); // 使用方法引用。与下一行相同
commands.put("bar", input -> this.bar(input));
commands.put("foobar", input -> this.foobar(input));
}
...
commands.get(command).accept(args); // Consumer的方法是accept
你可以在这里查看预定义的函数式接口。
英文:
You can use Consumer
instead of Runnable
. Consumer
accept a parameter and returns nothing. If you want also to return a param you need to use Function
.
So your code will look like this:
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
You can check predefined functional interfaces here.
答案2
得分: 0
除了使用Consumer<String[]>
之外,您还可以为您的命令定义一个自定义接口以进行实现:
public static interface Command {
public void run(String... args);
}
可按照您的预期使用:
private LinkedHashMap<String, Command> commands = new LinkedHashMap<>();
commands.put("foo", (input) -> foo(input));
不要害怕声明您自己的特定接口。
英文:
In addition to using Consumer<String[]>
, you can also define a custom interface for your commands to implement:
public static interface Command {
public void run(String ... args);
}
To use exactly as you would expect:
private LinkedHashMap<String, Command> commands = new LinkedHashMap<>();
commands.put("foo", (input) -> foo(input));
Don't be afraid of declaring your own specific interfaces.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论