Java Reference
In-Depth Information
39 7: areturn
41 private java.lang.String lambda$getSupplier$0(java.lang.String);
43 Code:
44 stack=2, locals=2, args_size=2
45 0: aload_0
46 1: aload_1
47 2: invokevirtual #3
// Method provideMessage:(Ljava/lang/String;)Ljava/lang/String;
48 5: areturn
49 }
Now things have gotten really quite strange. We see the example of the synthetic method with the
strange name: lambda$getSupplier$0(java.lang.String); . That is an instance method that takes a
String, and calls provideMessage on that string using the instance: it loads aload_0 , which is this , and
then aload_1 , which is the argument, and then does an invokevirtual . Our getSupplier method does
something similar: it loads the message and this , and then does our familiar invokedynamic call to generate
the supplier.
Note that we are passing in two arguments in our signature to invokedynamic now: all the variables
that we need to reference inside our lambda implementation become arguments to invokedynamic . This
is called “lambda lifting”: we lift the bound variables out of the lambda and make them arguments to
instantiating it. The signal that we have performed lambda lifting is the use of invokespecial as our means
of method invocation: we saw it used before to signal a constructor, but now we are using it as a signal to the
bootstrap method that we are having to construct a new instance with the lifted arguments.
The important part to realize is that declaring a lambda that uses variables is a lot like declaring a new
type that refers to those variables: we will end up instantiating a new instance that will hold onto those
variables, and it will end up referring to those member fields when it executes. Therefore, defining a lambda
inline and defining your own anonymous inner class have practically identical performance characteristics.
Use whichever approach is more natural for the space you are in.
Lambdas are extremely powerful, but under the hood, they are conceptually simple: the Java SDK has a
method which will take in your lambda and return an instance of whatever type your lambda is targeting. It
performs that binding just once using the invokedynamic bytecode instruction, and after the first execution,
it is equivalent to instantiating the interface implementation directly. If you define a lambda inline, then
you are going to get a synthetic method, but everything else is practically the same. There is a lot of noise
in the bytecode and some careful bookkeeping to make this all happen right, but the concept of the
implementation is actually rather simple. Most importantly, there is nothing innate in this implementation
that makes lambdas slow or heavyweight compared to other ways to solve the same problem: you are never
going to see significant improvements by rewriting lambdas into something else. The implementation is
smarter than that!
Search WWH ::

Custom Search