I am looking for a way to invoke multiple argument methods but using a lambda
construct. In the documentation it is said that lambda
is only usable if it can map to a functional interface.
I want to do something like:
test((arg0, arg1) -> me.call(arg0, arg1));
test((arg0, arg1, arg2) -> me.call(arg0, arg1, arg2));
...
Is there any way one can do this elegantly without defining 10 interfaces, one for each argument count?
Update
I use multiple interfaces extending from a non-method interface and I overload the method.
Example for two arguments:
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
}
I hope for a possibility to replace the 10 invoker interfaces and the 10 overloaded methods with a single solution.
I have a reasonable use case and please do not ask questions like 'Why would you do such a thing?' and 'What is the problem you are trying to solve?' or anything like that. Just know that I have thought this through and this is a legitimate problem I'm try to solve.
Sorry to add confusion calling it invoker but it is actually what it is called in my current use case (testing constructor contracts).
Basically, as stated above, think about a method that works with a different number of attributes within the lambda
.
Variable defined by the enclosing scope of a lambda expression are accessible within the lambda expression. For example, a lambda expression can use an instance or static variable defined by its enclosing class.
You can pass lambda expressions as arguments to a function. If you have to pass a lambda expression as a parameter, the parameter type should be able to hold it.
Lambda expressions can be stored in variables if the variable's type is an interface which has only one method. The lambda expression should have the same number of parameters and the same return type as that method.
Q 6 - Which of the following is correct about Java 8 lambda expression? A - Using lambda expression, you can refer to final variable or effectively final variable (which is assigned only once).
In Java
you need to use an array like this.
test((Object[] args) -> me.call(args));
If call
takes an array variable args
this will work. If not you can use reflection to make the call instead.
The final solution I currently use is defining a hierarchy of interfaces (as stated in the question) and use default methods to avoid failure. Pseudo code looks like this:
interface VarArgsRunnable {
default void run(Object ... arguments) {
throw new UnsupportedOperationException("not possible");
}
default int getNumberOfArguments() {
throw new UnsupportedOperationException("unknown");
}
}
and a interface for four arguments for instance:
@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;
}
}
Having defined 11 interfaces from VarArgsRunnable0 to VarArgsRunnable10 overloading a method becomes quite easy.
public void myMethod(VarArgsRunnable runnable, Object ... arguments) {
runnable.run(arguments);
}
Since Java can not compose a Lambda by finding the correct extended functional interface of VarArgsRunnable by using something like instance.myMethod((index, value) -> doSomething(to(index), to(value)), 10, "value")
one need to overload the method using the correct interface.
public void myMethod(VarArgsRunnable2 runnable, Object arg0, Object arg1) {
myMethod((VarArgsRunnable)runnable, combine(arg0, arg1));
}
private static Object [] combine(Object ... values) {
return values;
}
Since this requires to cast Object to any appropriated type using to(...)
one can go for parameterization using Generics in order to avoid this usage.
The to
-method looks like this:
public static T to(Object value) {
return (T)value; //Supress this warning
}
The example is lame but I use it to call a method with multiple arguments being a permutation of all potential combinations (for testing purposes) like:
run((index, value) -> doTheTestSequence(index, value), values(10, 11, 12), values("A", "B", "C"));
So this little line runs 6 invocations. So you see this is a neat helper being able to test multiple stuff in a single line instead of defining a lot more or use multiple methods in TestNG and whatever... .
PS: Having no need to use reflections is quite a good thing, since it can not fail and is quite save argument count wise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With