我正在寻找一种方法来调用多个参数方法,但使用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
中你需要使用这样的数组。
test((Object[] args) -> me.call(args));
如果call
采用数组变量args
,这将起作用。如果不是,您可以使用反射代替进行调用。
我目前使用的最终解决方案是定义接口层次结构(如问题中所述)并使用默认方法来避免失败。伪代码看起来像这样:
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:不需要使用反射是一件非常好的事情,因为它不会失败并且非常明智地保存参数。
我相信以下代码应该适合您想要的:
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)
。
我所做的是我自己的用例是定义一个接受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);