Lambdas in Java Bytecode
This chapter is for those who want to go deeper and understand how lambdas really work. This is your red
pill vs. blue pill moment: do you want to live in a world where Java has lambdas and they work great and
everything is slick and awesome, or do you want to see what is really going on beneath the surface and
discover the magic of the compiler pulls? If you are happy with the lambdas as you know and love them,
then skip this chapter and enjoy. If you need to know what your code really means, then hold on tight: it's
time to go down the rabbit hole.
The reality is that lambdas are an illusion. Java lambdas are entirely a construct of the compiler. The
compiler converts your Java code with its lambdas into more primitive operations within a lower-level
language. Understanding that conversion can help you understand exactly what is going on, which can be
useful in situations such as debugging and optimization.
However, before we step into this new world, there are some necessary caveats. This chapter is written
based on Oracle's Java compiler, and specifically version 1.8.0_05. It is entirely possible (although extremely
unlikely) that Oracle will completely overhaul their lambda implementation in a future release, and certainly
likely that there will be refinements as we go along. Alternative Java compilers might well have very different
implementations, as well. So if you are following along with your own Java compiler at home, and you notice
some subtle difference, then that is what happened. Those kinds of changes are what you have to expect to
see when you look under the hood.
We also need to be up front that this chapter is not a comprehensive introduction to Java compilers and
the resulting underlying bytecode language. That language has its own semantics, and those semantics are
tricky and technical. We will just start to get into some of those semantics through the course of this chapter.
We are staying focused on lambdas and what they do, so anything extraneous to lambdas is irrelevant to this
If you are still with me after all that warning, then you probably know that Java code itself does not
compile directly to bytes that are executed by the operating system. Instead, the Java compiler (henceforth,
“javac”) compiles to another intermediate form, which is executed by the Java Virtual Machine (henceforth,
“JVM”). This intermediate form is called the Java bytecode. Bytecode, however, has no concept of lambdas. It
also has no concept of try-with-resources blocks, enhanced for-loops, or many of the other structures within
Java. Instead, the compiler converts those Java structures into an underlying form in bytecode.
In this chapter, we will see how the compiler converts lambda commands in the Java language into
structures within the Java bytecode language. We will begin by getting comfortable with reading bytecode,
and then we will move on to seeing how particular kinds of lambda structures are converted into bytecode.
But before we get to the lambdas, we need to get the basics.