Well, lambda expressions are desugared into methods during compilation and as long as they don’t capture this
(don’t access non-static
members), these methods will be static
. The tricky part is to get to these methods as there is no inspectable connection between the functional interface instance and its target method.
To illustrate this, here the simplest case:
public class LambdaToMethod {
public static void legacyCaller(Object arg, Method m) {
System.out.println("calling Method \""+m.getName()+"\" reflectively");
try {
m.invoke(null, arg);
} catch(ReflectiveOperationException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) throws URISyntaxException
{
Consumer<String> consumer=s -> System.out.println("lambda called with "+s);
for(Method m: LambdaToMethod.class.getDeclaredMethods())
if(m.isSynthetic() && m.getName().contains("lambda")) {
legacyCaller("a string", m);
break;
}
}
}
This works smoothly as there is only one lambda expression and hence, one candidate method. The name of that method is compiler specific and may contain some serial numbers or hash codes, etc.
On kludge is to make the lambda expression serializable and inspect its serialized form:
static Method lambdaToMethod(Serializable lambda) {
for(Class<?> cl=lambda.getClass(); cl!=null; cl=cl.getSuperclass()) try {
Method m=cl.getDeclaredMethod("writeReplace");
m.setAccessible(true);
try {
SerializedLambda sl=(SerializedLambda)m.invoke(lambda);
return LambdaToMethod.class.getDeclaredMethod(sl.getImplMethodName(),
MethodType.fromMethodDescriptorString(sl.getImplMethodSignature(),
LambdaToMethod.class.getClassLoader()).parameterArray());
} catch(ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
} catch(NoSuchMethodException ex){}
throw new AssertionError();
}
public static void main(String[] args)
{
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("first lambda called with "+s)));
legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable)
s -> System.out.println("second lambda called with "+s)));
}
This works, however, serializable lambdas come at a high price.
The simplest solution would be to add an annotation to a parameter of the lambda expression to be found when iterating over the methods, however, currently, javac
doesn’t store the annotation properly, see also this question about this topic.
But you may also consider just creating ordinary static
methods holding the code instead of a lambda expression. Getting a Method
object for a method is straight-forward and you still can create a functional interface instance out of them using method references…