Lambda表达式与方法参考实现细节

鉴于这种:

class MyClass { static class A { public boolean property() { return Math.random() < 0.5; } } static List filterLambda(List list) { return list.stream().filter(a -> a.property()).collect(Collectors.toList()); } static List filterMethodCall(List list) { return list.stream().filter(A::property).collect(Collectors.toList()); } } 
  • 编译器对每种方法的作用有何不同?
  • 如果有的话,内存使用量或运行时间是否存在差异? (即使它很小,问题只是学术问题)

PD:我知道问题类似于这个问题,但我认为问题没有得到正确解决。

这是Brett Oken关联的Brian Goetz文档的摘录:

当编译器遇到lambda表达式时,它首先将lambda体降低(desugars)为一个方法,其参数列表和返回类型与lambda表达式匹配,可能还有一些额外的参数(对于从词法范围捕获的值,如果有的话)。 )在捕获lambda表达式的时刻,它会生成一个invokedynamic调用站点,在调用时,它返回lambda正在转换的函数接口的实例。 此调用站点称为给定lambda的lambda工厂。 lambda工厂的动态参数是从词法范围捕获的值。 lambda工厂的bootstrap方法是Java语言运行时库中的标准化方法,称为lambda metafactory。 静态引导参数在编译时捕获有关lambda的信息(它将被转换的function接口,desugared lambda体的方法句柄,有关SAM类型是否可序列化的信息等)

方法引用的处理方式与lambda表达式相同,只是大多数方法引用不需要被置于新方法中 ; 我们可以简单地为引用的方法加载一个常量方法句柄并将其传递给metafactory。

从同一文档中提取的示例:

例如,考虑一个捕获字段minSize的lambda:

 list.filter(e -> e.getSize() < minSize ) 

我们将此作为实例方法进行desugar,并将接收器作为第一个捕获的参数传递:

 list.forEach(INDY((MH(metaFactory), MH(invokeVirtual Predicate.apply), MH(invokeVirtual B.lambda$1))( this )))); private boolean lambda$1(Element e) { return e.getSize() < minSize; } 

 list.filter(String::isEmpty) 

翻译为:

 list.filter(indy(MH(metaFactory), MH(invokeVirtual Predicate.apply), MH(invokeVirtual String.isEmpty))())) 

这是方法引用的java语言规范。

这是lambda表达式的规范。

从规则的角度来看,Lambdas相当复杂。

但是,在这两种情况下,结果都是一个invokedynamic调用。

Brian Goetz写了一篇关于它是如何工作的文档 。