在Java中将参数传递给哈希映射中的lambda的最佳方法是什么。

huangapple go评论79阅读模式
英文:

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&lt;String, Runnable&gt; commands = new LinkedHashMap&lt;String, Runnable&gt;();

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

  private void handleCommand(String command) {
    if (!commands.containsKey(command)) {
      System.out.printf(&quot;&#39;%s&#39; is an invalid command%n&quot;, 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(&quot; &quot;);

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

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

and modifying the appropriate methods like so

    ...
    commands.put(&quot;foo&quot;, (args) -&gt; 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) =&gt; { ...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来替代RunnableConsumer接受一个参数并且不返回任何值。如果你想要返回一个参数,你需要使用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&lt;String, Consumer&lt;String[]&gt;&gt; commands = new LinkedHashMap&lt;&gt;();

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

....
commands.get(command).accept(args); // Consumer&#39;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&lt;String[]&gt;, 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&lt;String, Command&gt; commands = new LinkedHashMap&lt;&gt;();

commands.put(&quot;foo&quot;, (input) -&gt; foo(input));

Don't be afraid of declaring your own specific interfaces.

huangapple
  • 本文由 发表于 2020年10月7日 17:47:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/64241439.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定