将参数传递给 lambda 表达式 - Java

Pass parameter to lambda expression - Java

我的程序要求我接受用户输入,并根据这个输入执行一个方法。以下question/answer很好地描述了我的基本想法:

How to call a method stored in a HashMap? (Java)

为此,我创建了一个 lambda 表达式数组:

public final Runnable[] userCommandMethods = {

        () -> userCommand1(),
        () -> userCommand2(),
    };

还有一组键:

public final String[] userCommandKeys = {

        commandKey1,
        commandKey2,
    };

使用以下方法连接创建HashMap:

public Map<String, Runnable> mapArrays (String[] array1, Runnable[] array2) {

        Map<String, Runnable> mappedArrays = new HashMap<String, Runnable>();

        for (int i = 0; i < array1.length; i ++) {

            mappedArrays.put(array1[i], array2[i]);
        }
        return mappedArrays;
    }

当我尝试使用 myHashMap.get(userInput).run(); 运行 一个方法时,它完美地工作,前提是 userCommandMethods 中的 none 方法需要输入参数。

我的问题:

如何将输入参数(特别是哈希映射)传递到 userCommandMethods 中包含的方法?

userCommand1() 方法接受输入参数,但 lambda 表达式不接受时,出现以下错误:

The method userCommand1(Map<String, String>) in the type ProgramCommands is not applicable for the arguments ()

但是,当我将参数传递给该方法时,它指出无法将其解析为变量。

编辑:详述:

userCommand1() 方法不带参数时:

public void userCommand1 () {

      // Do some stuff
}

它工作得很好。但是,如果该方法确实采用输入参数,我不确定如何使用 lambda 表达式:

public void userCommand1 (Map<String, String> myMap) {

      // Do some stuff
}

这是你的想法吗?


interface Command<T> {
   public void run(T arg);
}

class SayHelloCommand implements Command<String>{
   public void run(String name){
      System.out.println("hello " + name);
   }
}

class CountCommand implements Command<Integer>{
   public void run(Integer limit){
      for(int i=0; i<=limit; i++)
        System.out.println(i);
   }
}

public class Main{
    public static void main(String[] args) {
        Command[] commands = new Command[3];
        commands[0] = new SayHelloCommand();
        commands[1] = new CountCommand();
        
        commands[0].run("Joe");
        commands[1].run(5);
    }
}

您只需要选择另一个功能界面(不是Runnable)。

例如,如果您的方法都采用 String 参数,则您应该使用 Consumer<String>. If they take a String and an int, then you should use BiConsumer<String, Integer>. If your methods need more than 2 parameters, you need to create your own functional interface. For an example, see my answer .

// use a list instead of an array, because arrays don't work well with generic types 
public final List<Consumer<String>> userCommandMethods = List.of(
    x -> userCommand1(x),
    x -> userCommand2() // it's fine if the method takes fewer parameters
);

而不是 run,你会调用 accept,这就是 ConsumerBiConsumer 的单一抽象方法的调用。

请注意,您还可以使用方法引用语法。如果 userCommand1 是静态的,x -> userCommand1(x) 可以重写为 SomeClass::userCommand1,其中 SomeClassuserCommand1 的封闭 class。如果 userCommand1 是非静态的,可以改写为 this::userCommand1.

您不需要从两个数组构建地图。您可以使用 ofEntries and entry 内联写入条目。

private final Map<String, Consumer<String>> someMap = Map.ofEntries(
    Map.entry("foo", SomeClass::userCommand1),
    Map.entry("bar", SomeClass::userCommand2),
    Map.entry("baz", SomeClass::userCommand3),
    // and so on
)

您正在使用不接受输入参数的 Runnable 接口:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

相反,您可以定义自定义界面并使用它。

举个简单的例子:

@FunctionalInterface
public interface RunnableWithArg {    
    void apply(String t) throws RuntimeException;    
}

实现可能如下所示:

public class RunnableTest {

    //also fine:
    //public final RunnableWithArg[] userCommandMethods = { t -> this.userCommand1(t), t -> this.userCommand2(t) };
    
    public final RunnableWithArg[] userCommandMethods = { this::userCommand1, this::userCommand2 };

    public String commandKey1 = "commandKey1";
    public String commandKey2 = "commandKey2";

    public final String[] userCommandKeys = { commandKey1, commandKey2, };

    public Map<String, RunnableWithArg> mapArrays(String[] array1, RunnableWithArg[] array2) {

        Map<String, RunnableWithArg> mappedArrays = new HashMap<>();

        for (int i = 0; i < array1.length; i++) {

            mappedArrays.put(array1[i], array2[i]);
        }
        return mappedArrays;
    }

    public void userCommand1(String data) {
        System.out.println("userCommand1 called with " + data);
    }

    public void userCommand2(String data) {
        System.out.println("userCommand2 called with " + data);
    }
    
    public void test()
    {
        var fncMap = mapArrays(userCommandKeys, userCommandMethods);
        
        for(String key: fncMap.keySet())
        {
            var fnc = fncMap.get(key);
            fnc.apply(key);
        }
    }
}

当然你也可以像这样定义一些通用类型的“@FunctionalInterface”,这样你就可以用它来获取输入和返回一些通用类型的输出:

@FunctionalInterface
public interface AbcFunction<T, R> {

    R apply(T t) throws AbcException;

    static <T> Function<T, T> identity() {
        return t -> t;
    }    
}