Java 8:具有可变参数的Lambda

Java 8:具有可变参数的Lambda,java,lambda,java-8,Java,Lambda,Java 8,我正在寻找一种调用多个参数方法的方法,但使用lambda构造。文档中说,lambda只有在能够映射到功能接口时才可用 public void myMethod(VarArgsRunnable2 runnable, Object arg0, Object arg1) { myMethod((VarArgsRunnable)runnable, combine(arg0, arg1)); } private static Object [] combine(Object ... values

我正在寻找一种调用多个参数方法的方法,但使用
lambda
构造。文档中说,
lambda
只有在能够映射到功能接口时才可用

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

private static Object [] combine(Object ... values) {
    return values;
}
我想做一些类似的事情:

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
}
Consumer<Object[]> add = args -> {
    int sum = 0;
    for (Object arg : args)
        sum += (int) arg;
    System.out.println(sum);
};
我希望有可能用一个解决方案替换10个调用器接口和10个重载方法

我有一个合理的用例,请不要问诸如“你为什么要做这样的事情?”和“你试图解决的问题是什么?”之类的问题。只要知道我已经想清楚了,这是我试图解决的一个合法问题

很抱歉,在调用它invoker时添加了混淆,但实际上它是在我当前的用例(测试构造函数契约)中调用的


基本上,如上所述,考虑一种方法,该方法在
lambda

中使用不同数量的属性。在
Java
中,您需要使用这样的数组

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

如果
call
接受一个数组变量
args
,这将起作用。如果没有,您可以使用反射来进行调用。

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

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)
一样。我当前使用的最终解决方案是定义接口的层次结构(如问题中所述),并使用默认方法避免失败。伪代码如下所示:

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的正确扩展函数接口,因此需要使用正确的接口重载方法

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

private static Object [] combine(Object ... values) {
    return values;
}
由于这需要使用
将对象强制转换为任何适当的类型,因此可以使用泛型进行参数化,以避免这种用法

to
-方法如下所示: 公共静态T到(对象值){ 返回(T)值;//禁止此警告 }

该示例很蹩脚,但我使用它来调用一个方法,其中多个参数是所有潜在组合的排列(出于测试目的),如:

所以这一小行运行6次调用。因此,您可以看到这是一个很好的助手,它能够在一行中测试多个内容,而不是在TestNG中定义更多内容或使用多个方法等等


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

我所做的是为我自己的用例定义一个接受varargs然后调用lambda的helper方法。我的目标是1)能够在一个方法中定义一个函数以实现简洁性和作用域(即lambda),2)非常简洁地调用该lambda。最初的海报可能也有类似的目的,因为他在上面的一条评论中提到,希望避免为每个调用编写Object[]{…}的冗长。也许这对其他人有用

步骤#1:定义助手方法:

public static void accept(Consumer<Object[]> invokeMe, Object... args) {
    invokeMe.accept(args);
}
对 您不需要重载方法或多个接口。可以使用带有变量参数列表方法的单个函数接口来完成

但是,由于Java varargs是使用隐式数组实现的,因此lambda将接受一个通用数组参数,并且必须处理从数组中解包参数的操作

如果函数的参数不都属于同一个类,则还必须处理类型转换,这会带来所有固有的危险

例子
包示例;
导入java.util.array;
导入java.util.List;
公共班机{
@功能接口
公共接口调用程序{
R调用(T…args);
}
@安全变量
公共静态无效测试(调用者调用者,T…args){
System.out.println(“测试结果:+invoker.invoke(args));
}
公共静态双除法(整数a、整数b){
返回a/(双)b;
}
公共静态字符串异构func(双a、布尔b、列表c){
返回a.toString()+”,“+b.hashCode()+”,“+c.size();
}
公共静态void main(字符串[]args){
Invoker divideInvoker=argArray->Main.divide(
argArray[0],argArray[1]
);
测试(divideInvoker,22,7);
调用程序weirdInvoker=argArray->heterogeneousFunc(
(双精度)argArray[0],(布尔值)argArray[1],(列表)argArray[2]
);
测试(weirdInvoker,1.23456d,Boolean.TRUE,Arrays.asList(“a”、“b”、“c”、“d”));
测试(weirdInvoker,Boolean.FALSE,Arrays.asList(1,2,3),9.99999d);
}
}
输出:

Test result: 3.142857142857143
Test result: 1.23456, 1231, 4
Exception in thread "main" java.lang.ClassCastException: class java.lang.Boolean cannot be cast to class 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)

我很好奇,对于数量可变的参数,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");
    }
}
公共类示例{
公共接口操作{
公共无效接受(T…ts);
}
公共静态void main(字符串参数[]){
//动作a=(字符串…x)->{也可以工作
动作a=(x)->{
for(字符串s:x){
系统输出打印项次;
}
};
a、 接受(“你好”,“世界”);
}
}
我知道这个问题已经得到了回答。这主要是为了那些喜欢古玩的人
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");
    }
}