Java › Functional Interfaces & Method References
How do lambda expressions get compiled at the bytecode level using invokedynamic, and how does this differ from how an anonymous inner class implementing the same functional interface would be compiled?
AdvancedAnswer
Unlike an anonymous inner class (which the compiler generates as an entirely separate .class file at compile time, instantiated via a normal constructor call), a lambda expression is compiled using the invokedynamic bytecode instruction combined with the LambdaMetafactory, which defers the actual creation of the implementing class until the lambda expression is FIRST executed at runtime (using a bootstrap method to dynamically generate and link an appropriate implementation class on demand, cached for subsequent uses) -- this approach avoids the class-file bloat of generating a separate named class for every single lambda in a codebase, and can offer performance benefits since the JVM has flexibility in how it implements the actual lambda dispatch, rather than being locked into the fixed structure an anonymous inner class's compiled bytecode represents.
// An anonymous inner class generates a separate class file at COMPILE time:
// Outer$1.class
Runnable r1 = new Runnable() { public void run() { System.out.println("anon"); } };
// A lambda instead uses invokedynamic, with the actual implementing class
// generated lazily at RUNTIME on first use, via LambdaMetafactory
Runnable r2 = () -> System.out.println("lambda");Real-world example A codebase with hundreds of lambda expressions doesn't suffer the same class-file bloat (hundreds of separate compiled .class files) that an equivalent codebase using anonymous inner classes for the same purpose would produce, since lambdas' implementing classes are generated dynamically at runtime rather than statically at compile time for each individual usage site.