How to get the name of the method resulting from a lambda

Viewed 5014

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:

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.

1 Answers

This is subject to change from version to version - there is no specification that says what the exact names would be - and it's done on purpose; well to protect against code that might find clever things to do with these names.

Related