If you have an anonymous class like
Predicate<String> isEmpty = new Predicate<String>() { public boolean test(String t) { return t.isEmpty(); } };
A library which is passed the reference to isEmpty
can inspect the byte code to see what it does and possibly manipulate it. Is there a way you can do this for lambdas?
Predicate<String> isEmpty = String::isEmpty;
e.g Say have this code and byte code
public class Main { public static void test(Predicate<String> tester) { System.out.println("tester.getClass()= " + tester.getClass()); System.out.println("tester.getClass().getClassLoader()="+ tester.getClass().getClassLoader()); } public static void main(String... args) { Predicate<String> isEmpty = String::isEmpty; test(isEmpty); } } $ javap -cp . -c -private Main.class Compiled from "Main.java" public class Main { public Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void test(java.util.function.Predicate<java.lang.String>); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: ldc #5 // String tester.getClass()= 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_0 16: invokevirtual #7 // Method java/lang/Object.getClass:()Ljava/lang/Class; 19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 22: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 25: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 31: new #3 // class java/lang/StringBuilder 34: dup 35: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 38: ldc #11 // String tester.getClass().getClassLoader()= 40: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 43: aload_0 44: invokevirtual #7 // Method java/lang/Object.getClass:()Ljava/lang/Class; 47: invokevirtual #12 // Method java/lang/Class.getClassLoader:()Ljava/lang/ClassLoader; 50: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 53: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 56: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 59: return public static void main(java.lang.String...); Code: 0: invokedynamic #13, 0 // InvokeDynamic #0:test:()Ljava/util/function/Predicate; 5: astore_1 6: aload_1 7: invokestatic #14 // Method test:(Ljava/util/function/Predicate;)V 10: return }
With a reference to tester
in test
how do I find which method is called?
For Lambda expressions, the compiler doesn't translate them into something which is already understood by JVM. Lambda syntax that is written by the developer is desugared into JVM level instructions generated during compilation, which means the actual responsibility of constructing lambda is deferred to runtime.
Also, we can see various details of the bytecode like constant pool, fields, and methods using the Jclasslib plugin dialog: Similarly, we have the Bytecode Visualizer Plugin to view the bytecode of a class file using the Eclipse IDE.
JVM (Java Virtual Machine) is an abstract machine. It is a specification that provides runtime environment in which java bytecode can be executed.
Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.
If you just want to SEE the bytecode:
javap -c -p -v classfile ^disassemble ^private methods ^verbose, including constant pool and bootstrap methods attribute
But if you want to try to do this at runtime, you're pretty much out of luck (by design, we don't have anything like Expression Trees), as the other answer suggests.
The simple answer is: You can't. There is a related answer of Brian Goetz on this matter. For implementing lambda expressions, javac creates an INVOKEDYNAMIC
instruction which delegates the invocation to the LambdaMetafactory
's bootstrap method. For the OpenJDK, this bootstrap method is then creating an implementation of the required interface at runtime using ASM.
Within the test
method, the retreived instance tester
is of this ASM-generated class such that you have no class file to read for finding out which method the Predicate
represents. In the general case, the exact decision of how to map lambda expressions to interface implementations is left to the runtime environment what makes your problem even harder. The only way to find out which method a lambda expression represents is to read the byte code of its creation, i.e. interpreting the main
method, for your example.
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