I am currently using Java reflection
I don\'t have any problem doing it with reflection . I learned about LambdaMetaFactory has better performance than reflection
You can’t bind an interface with a varargs method to an arbitrary target method, as then, the interface would promise to handle an arbitrary number of arguments while the actual implementation method only accepts a fixed number of arguments. Hence, the following does not compile:
public interface MethodFunctionArgs<T> {
T call(Object... params);
static String someMethod(String arg1, int arg2) { return ""; }
// does not work
MethodFunctionArgs<String> func = MethodFunctionArgs::someMethod;
}
What you can do, is the other way round, an implementation method that can handle an arbitrary number of arguments can also handle a request with specific arguments:
public interface MethodFunctionArgs<T> {
T call(String arg1, int arg2);
static String someMethod(Object... params) { return ""; }
// no problem
MethodFunctionArgs<String> func = MethodFunctionArgs::someMethod;
}
But it must be mentioned that the LambdaMetaFactory
is not capable of handling the varargs processing. The compiler helps here by inserting a synthetic helper method, so the compiled code is equivalent to
public interface MethodFunctionArgs<T> {
T call(String arg1, int arg2);
static String someMethod(Object... params) { return ""; }
// no problem
MethodFunctionArgs<String> func = (arg1,arg2) -> someMethod(new Object[]{arg1, arg2});
}
But when you have a functional interface with varargs, you are already paying a lot of the typical Reflection costs (boxing, and creating and filling an array) before even invoking the interface method. You may still test the performance of Method.invoke
compared to MethodHandle.invoke
:
private MethodHandle handle;
public Object callReturn(Object... args) throws Exception {
try {
if(handle == null) {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle h = lookup.unreflect(this.getMethod());
handle = h.asType(h.type().generic())
.asSpreader(Object[].class, h.type().parameterCount());
}
return handle.invokeExact(args);
} catch (Throwable ex) {
throw new Exception(ex);
}
}
To get a performance benefit from the LambdaMetaFactory
, you need a specific interface with a matching functional signature.
interface SpecialFunction {
Map<String,SpecialFunction> PREDEFINED = getMap();
String call(int i, double d, String s);
static String method1(int i, double d, String s) {
return "method1("+i+", "+d+", "+s+')';
}
static String method2(int i, double d, String s) {
return "method2("+i+", "+d+", "+s+')';
}
static String method3(int i, double d, String s) {
return "method3("+i+", "+d+", "+s+')';
}
/* private (with Java 9) */ static Map<String,SpecialFunction> getMap() {
Map<String,SpecialFunction> map = new HashMap<>();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType invoked = MethodType.methodType(SpecialFunction.class);
MethodType func = MethodType.methodType(String.class,
int.class, double.class, String.class);
final int mod = Modifier.PUBLIC|Modifier.STATIC;
for(Method m: SpecialFunction.class.getDeclaredMethods()) try {
MethodHandle target = lookup.unreflect(m);
if((m.getModifiers()&mod) == mod && target.type().equals(func))
map.put(m.getName(), (SpecialFunction)LambdaMetafactory.metafactory(
lookup, "call", invoked, func, target, func).getTarget().invoke());
} catch(Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
return Collections.unmodifiableMap(map);
}
}