From a simple point of view, lambda expressions are compiled into static
methods, since they don’t capture this
(i.e. do not access instance members).
public class App {
public static void foo(){
Consumer<Integer> c1 = n -> {};
Consumer<Integer> c2 = n -> {};
}
public static void main(String [] args) {
Supplier<String> sup1 = () -> "I am sup1";
Supplier<String> sup2 = () -> "I am sup2";
Supplier<String> sup3 = () -> "I am sup3";
Stream.of(App.class.getDeclaredMethods()).forEach(System.out::println);
}
}
Running the previous example and checking the generated bytecodes with the javap
tool, we can see that lambdas referred by variables c1
, c2
, sup1
, sup2
, sup3
are compiled to methods with names:
lambda$foo$0
lambda$foo$1
lambda$main$2
lambda$main$3
lambda$main$4
However, if we print the getClass()
for each object referred by those variables (c1
, c2
, sup1
, sup2
, sup3
) we will get, respectively:
class App$$Lambda$5/1705736037
class App$$Lambda$6/455659002
class App$$Lambda$1/791452441
class App$$Lambda$2/531885035
class App$$Lambda$3/1418481495
So, how can we make the correspondence between the runtime class names and the name of the methods resulting from the lambdas?
UPDATE
None of the solutions pointed in the following duplicated questions helps me to solve the problem that I am asking in my question:
- How to get the MethodInfo of a Java 8 method reference?
- Printing debug info on errors with java 8 lambda expressions 1 answer
- Get MethodHandle from lambda object
The only way that I found was to adapt the solution of @Holger from its answer (not the accepted one) to the question Java 8: convert lambda to a Method instance with clousure included. However, in my case, instead of a Method
object I am just looking for the name of the method. So, adapting that lambdaToMethod()
to the following methodNameFromLambda()
I got a solution:
static String methodNameFromLambda(Serializable lambda) {
try {
Method m = lambda.getClass().getDeclaredMethod("writeReplace");
m.setAccessible(true);
SerializedLambda sl=(SerializedLambda)m.invoke(lambda);
return sl.getImplMethodName();
} catch(ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
Now, lambdas of the example must be cast to Consumer<Integer>&Serializable
or Supplier<String>&Serializable
as describe in @Holger answer.
I am not aware if this solution is correct, but it works for my example and for the cases that I am working on.
writeReplace
method is within the actual class. It would be a reasonable strategy to let lambdas inherit that method from a common base class instead of re-implementing it in every generated class, so I made the code ready to handle this scenario. To be pessimistic, there is no guaranty for the presence of this method at all, e.g. a different JRE could implement the replacement within the depths of theObjectOutputStream
implementation… – Willem