Java 8:具有可变参数的Lambda

时间:2015-08-25 16:44:49

标签: java lambda java-8

我正在寻找一种方法来调用多个参数方法但使用lambda构造。在文档中,据说lambda仅在可以映射到功能界面时才可用。

我想做类似的事情:

test((arg0, arg1) -> me.call(arg0, arg1));
test((arg0, arg1, arg2) -> me.call(arg0, arg1, arg2));
...

如果没有定义10个接口,每个参数计数一个,有没有办法可以优雅地做到这一点?

更新

我使用从非方法接口扩展的多个接口,并重载该方法。

两个参数的示例:

interface Invoker {}
interface Invoker2 extends Invoker { void invoke(Object arg0, Object arg1);}
void test(Invoker2 invoker, Object ... arguments) {
    test((Invoker)invoker, Object ... arguments);
}

void test(Invoker invoker, Object ... arguments) {
    //Use Reflection or whatever to access the provided invoker
}

我希望有可能用一个解决方案替换10个调用者接口和10个重载方法。

我有一个合理的用例,请不要问诸如“为什么要做这样的事情?”之类的问题?'和'你想解决的问题是什么?'或类似的东西。只要知道我已经考虑过这一点,这是我试图解决的合法问题。

很抱歉添加混淆调用它的调用程序,但它实际上是在我当前的用例(测试构造函数合同)中调用它。

基本上,如上所述,考虑在lambda内使用不同数量属性的方法。

5 个答案:

答案 0 :(得分:9)

Java中你需要使用这样的数组。

test((Object[] args) -> me.call(args));

如果call采用数组变量args,这将有效。如果不是,您可以使用反射代替进行调用。

答案 1 :(得分:2)

我很好奇一个可变数目的参数的lambda是否可以工作。确实确实如此。参见:

public class Example {
    public interface Action<T> {
        public void accept(T... ts);
    }

    public static void main(String args[]) {
        // Action<String> a = (String... x) -> { also works
        Action<String> a = (x) -> {
            for(String s : x) {
                System.out.println(s);
            }
        };

        a.accept("Hello", "World");
    }
}

我知道问题已经回答。这主要是针对那些好奇并遇到这篇文章的人。

答案 2 :(得分:0)

我认为以下代码应该适合您的需求:

public class Main {
    interface Invoker {
      void invoke(Object ... args);
    }

    public static void main(String[] strs) {
        Invoker printer = new Invoker() {
            public void invoke(Object ... args){
                for (Object arg: args) {
                    System.out.println(arg);
                }
            }
        };

        printer.invoke("I", "am", "printing");
        invokeInvoker(printer, "Also", "printing");
        applyWithStillAndPrinting(printer);
        applyWithStillAndPrinting((Object ... args) -> System.out.println("Not done"));
        applyWithStillAndPrinting(printer::invoke);
    }

    public static void invokeInvoker(Invoker invoker, Object ... args) {
        invoker.invoke(args);
    }

    public static void applyWithStillAndPrinting(Invoker invoker) {
        invoker.invoke("Still", "Printing"); 
    }
}

请注意,您不必创建lambda并将其传递给me.call,因为您已经有了对该方法的引用。您可以像致电test(me::call)一样致电applyWithStillAndPrinting(printer::invoke)

答案 3 :(得分:0)

对于我自己的用例,我要做的是定义一个辅助方法,该方法接受varargs,然后调用lambda。我的目标是1)能够在简洁和范围界定的方法(即lambda)中定义一个函数,以及2)使对该lambda的调用非常简洁。自从他在上面的评论之一中提到,想要避免避免为每次调用编写Object [] {...}的冗长性,原始的发帖人可能有类似的目标。也许这对其他人有用。

步骤1:定义辅助方法:

public static void accept(Consumer<Object[]> invokeMe, Object... args) {
    invokeMe.accept(args);
}

步骤2:定义可以使用不同数量参数的lambda:

Consumer<Object[]> add = args -> {
    int sum = 0;
    for (Object arg : args)
        sum += (int) arg;
    System.out.println(sum);
};

步骤3:多次调用lambda-这种简洁性是我想要语法糖的原因:

accept(add, 1);
accept(add, 1, 2);
accept(add, 1, 2, 3);
accept(add, 1, 2, 3, 4);
accept(add, 1, 2, 3, 4, 5);
accept(add, 1, 2, 3, 4, 5, 6);

答案 4 :(得分:0)

您不需要辅助方法,多个接口或任何其他行李。

但是,由于Java varargs是使用隐式数组实现的,因此您的lambda将采用单个数组参数,并且必须处理参数数组的拆包。

如果您的函数所具有的参数不完全属于同一类,则还必须处理类型转换,并带来所有固有的危险。

示例

package example;
import java.util.Arrays;
import java.util.List;

public class Main {
    @FunctionalInterface
    public interface Invoker<T, R> {
        R invoke(T... args);
    }

    @SafeVarargs
    public static <T, R> void test(Invoker<T, R> invoker, T...args) {
        System.out.println("Test result: " + invoker.invoke(args).toString());
    }

    public static Double divide(Integer a, Integer b) {
        return a / (double)b;
    }

    public static String heterogeneousFunc(Double a, Boolean b, List<String> c) {
        return a.toString() + " " + b.hashCode() + " " + c.size();
    }
    public static void main(String[] args) {
        Invoker<Integer, Double> invoker = argArray -> Main.divide(argArray[0], argArray[1]);
        test(invoker, 22, 7);

        Invoker<Object, String> weirdInvoker = argArray -> heterogeneousFunc(
                (Double) argArray[0], (Boolean) argArray[1], (List<String>) argArray[2]);

        test(weirdInvoker, 1.23456d, Boolean.TRUE, Arrays.asList("a", "b", "c", "d"));

        test(weirdInvoker, Boolean.FALSE, Arrays.asList(1, 2, 3), 9.999999d);
    }
}

输出:

Test result: 3.142857142857143
Test result: 1.23456 1231 4
Exception in thread "main" java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Double
    at example.Main.lambda$main$1(Main.java:27)
    at example.Main.test(Main.java:13)
    at example.Main.main(Main.java:32)