Java 8:具有可变参数的Lambda

问题描述 投票:14回答:4

我正在寻找一种方法来调用多个参数方法,但使用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中使用不同数量属性的方法。

java lambda java-8
4个回答
9
投票

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

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

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


2
投票

我目前使用的最终解决方案是定义接口层次结构(如问题中所述)并使用默认方法来避免失败。伪代码看起来像这样:

interface VarArgsRunnable {
     default void run(Object ... arguments) {
          throw new UnsupportedOperationException("not possible");
     }
     default int getNumberOfArguments() {
          throw new UnsupportedOperationException("unknown");
     }
}

和一个例如四个参数的接口:

@FunctionalInterface
interface VarArgsRunnable4 extends VarArgsRunnable {
     @Override
     default void run(Object ... arguments) {
          assert(arguments.length == 4);
          run(arguments[0], arguments[1], arguments[2], arguments[3]);
     }

     void run(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4);

     @Override
     default int getNumberOfArguments() {
          return 4;
     }
}

定义了从VarArgsRunnable0到VarArgsRunnable10的11个接口重载方法变得非常容易。

public void myMethod(VarArgsRunnable runnable, Object ... arguments) {
     runnable.run(arguments);
}

由于Java无法通过使用instance.myMethod((index, value) -> doSomething(to(index), to(value)), 10, "value")之类的东西找到VarArgsRunnable的正确扩展功能接口来组成Lambda,因此需要使用正确的接口重载该方法。

public void myMethod(VarArgsRunnable2 runnable, Object arg0, Object arg1) {
    myMethod((VarArgsRunnable)runnable, combine(arg0, arg1));
}

private static Object [] combine(Object ... values) {
    return values;
}

由于这需要使用to(...)将Object转换为任何适当的类型,因此可以使用泛型进行参数化以避免这种用法。

to方法如下所示:public static T to(Object value){return(T)value; //取消此警告}

这个例子很蹩脚,但我用它来调用一个方法,多个参数是所有可能组合的排列(用于测试目的),如:

run((index, value) -> doTheTestSequence(index, value), values(10, 11, 12), values("A", "B", "C"));

所以这条小线路运行了6次调用。所以你看到这是一个巧妙的帮手,可以在一行中测试多个东西,而不是定义更多或在TestNG中使用多个方法等等。

PS:不需要使用反射是一件非常好的事情,因为它不会失败并且非常明智地保存参数。


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)


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);
};

第三步:多次调用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);
© www.soinside.com 2019 - 2024. All rights reserved.