"Real lambdas" with the full invokedynamic treatment is a big job, and requires static type inference.
Given that Groovy can already coerce closures into functional interfaces (dynamically), we could implement the whole metafactory-stuff with the static compilation, and as a separate effort, right?
Also: Fantastic job, Daniel!
You can even use the LambdaMetaFactory at runtime, if you propagate the target type to the point you create the lambda, currently it's two operations in groovy, create the closure and then convert it to a functional interface, but it can be done in one operation, this is what Nashorn does. But you don't have to do that now. It's more an optimization than anything else. The thing is that if you you the lambda metafactory, you get two optimizations: if the lambda doesn't capture any values, the lambda is seen by the JIT as a constant, this optimization is important mostly for the stream API because the JIT can inline all the lambda code at the location you use the stream (otherwise, it depends if the escape analysis is in a good day or not). The second optimization is that if the lambda capture values these values are seen as field that can not be changed after the creation of the lambda (like true final fields that the reflection can not change). This second optimization is less important and just nice to have.
Also, you can emulate what the lambda metafactory does in the MOP2, but it requires to use Unsafe (a part which is not removed in 9) which is no a good strategy on the long term, it's better to delegate the creation of the lambda to the metafactory to the optimizations for free.
The static compilation will make easier to see the creation of the closure and its cast to a functional interface as one operation, enabling to use the lambda metafactory but as i said above, you can use the lambda metafactory at runtime.
I assumed so, but wanted to be sure :-)