The previous articles discussed functional interfaces and lambda expression syntax, invokedynamic directives, and how Groovy2 uses Indy directives. This article is based on a few previous articles and briefly describes how the JAVA8 layer implements lambda expressions.
Sample code
This article takes the following code as an example to expand the discussion:
Import Java.util.arrays;import Java.util.list;public class Lambdaimpltest {public static void Main (string[] args) { M1 (Arrays.aslist (args)); } public static void M1 (List<string> List) { List.sort ((A, B) a.length ()-b.length ());} }
Insert invokedynamic directive
It is certainly possible to implement lambda expressions directly using anonymous classes , so that lambda expressions are just the syntax sugar of the Java compiler. But instead of doing this, JAVA8 is using a more complex (and more flexible) approach: using Indy directives. Obviously, this approach needs to be implemented by the compiler and the JVM together. The compiler inserts a indy instruction where each lambda expression appears, and also generates information such as the corresponding Constant_invokedynamic_info constant pool entry and Bootstrapmethods property in the class file. This information points the bootstrap method of this indy instruction to Lambdametafactory.metafactory (...). Method.
Inserting the Lambda$x&y method
Compile the Lambdaimpltest.java with Javac , and then use javap-v-P to decompile the. class file, and you can see that the compiler generated a lambda$m1$0 method, and moved the contents of the lambda expression to the inside:
The compiler generates methods for lambda expressions in accordance with certain rules to ensure that these methods do not have duplicate names. If you restore the bytecode to Java code, Lambdaimpltest will look like this:
public class Lambdaimpltest {public static void Main (string[] args) { m1 (Arrays.aslist (args)); } public static void M1 (List<string> List) { list.sort (/*lambda*/); } private static int lambda$m1$0 (string A, string b) { return a.length ()-b.length (); } }
Lambdametafactory.metafactory (...) Method
When the JVM first executes to this indy instruction, it will find the corresponding bootstrap method of this instruction, then call the method and get a callsite. The following is the code for the Metafactory () method:
public static CallSite Metafactory (Methodhandles.lookup caller, String Invokedname, Methodtype Invokedtype, Methodtype Sammethodtype, Methodhandle Implmethod, Methodtype Instantiatedmethodtype) throws Lambdaconversionexception {Abstractvalidatin Glambdametafactory MF; MF = new Innerclasslambdametafactory (caller, Invokedtype, Invokedname, Sammeth Odtype, Implmethod, Instantiatedmethodtype, False, Empty_class_array, Empty_mt_array); Mf.validatemetafactoryargs (); return Mf.buildcallsite (); }
As you can guess from the code, the JAVA8 internally also implements the lambda expression in the same way as the inner class. and Innerclasslambdametafactory's Buildcallsite () method proves this, the Buildcallsite () method is too long, there is no code, in short, it will call a called Spininnerclass () method, it is this method that uses the bytecode tool to generate a class in memory.
Observe the classes generated by Spininnerclass ()
If we set the system property "Jdk.internal.lambda.dumpProxyClasses" When we start the JVM, the Spinnerclass () method will save the generated class to a file for easy debugging. If we run lambdaimpltest with the following command, we can see this class in the "Current directory":
Java-djdk.internal.lambda.dumpproxyclasses Lambdaimpltest
lambdaimpltest$ $Lambda $1.class
You can also use the JAVAP command to decompile the class file, the following is the anti-compilation result (I myself turned the JAVAP result into a Java file):
Final class lambdaimpltest$ $Lambda $ implements Java.util.Comparator { private lambdaimpltest$ $Lambda $ () { } public int Compare (object A, object B) { return lambdaimpltest.lambda$m1$0: ((String) A, (string) b);} }
It can be seen that this inner class implements the comparator interface, and the Compare () method simply calls the Lambda$m1$0 () method. Continuing the analysis of the Buildcallsite () method, the JVM then instantiates an instance of the inner class and then creates a constantcallsite whose target methodhandle points to the Compare () method of the inner class instance.
The word description is difficult to understand, I drew a picture below:
Summarize
The Java internal implementation of the simplest lambda expression is analyzed above, which can be slightly more complicated if the lambda expression captures external parameters. But the conclusion remains the same: The JAVA8 is also implemented in the form of an inner class to implement lambda expressions.