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

时间:2015-03-24 22:49:36

标签: java lambda

鉴于此:

class MyClass {
    static class A {
        public boolean property() {
            return Math.random() < 0.5;
        }
    }

    static List<A> filterLambda(List<A> list) {
        return list.stream().filter(a -> a.property()).collect(Collectors.toList());
    }

    static List<A> filterMethodCall(List<A> list) {
        return list.stream().filter(A::property).collect(Collectors.toList());
    }
}
  • 每个方法编译器的作用有什么不同?
  • 如果有,是否存在内存使用或运行时间的差异? (即使它很小,问题只是学术问题)
PD:我知道这个问题与this one类似,但我认为它没有得到正确解决。

3 个答案:

答案 0 :(得分:5)

这是Brett Oken关联的Brian Goetz's doc的摘录:

  

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

     

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

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

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

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

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

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))()))

答案 1 :(得分:1)

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

Here是lambda表达式的规范。

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

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

Brian Goetz写了doc如何运作。

答案 2 :(得分:1)

在其中我们可以找到差异lambda表达式和方法引用的一种情况是将其用作Supplier接口的一部分。可以说我们有下面的静态方法

public static <T, E> T catchException(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return  result ;
    } catch (Exception e) {
        System.out.println("Exception");
        return null;
    }
}

当我们使用如下所示的lambda表达式调用该方法时,代码可以正常工作,因为lambda表达式作为Supplier的一部分传递并仅在捕获异常的get()方法被调用时执行。

List<String> underlyers=null;
System.out.println(catchException(()->underlyers.size()));

当我们使用如下所示的方法引用来调用方法时,在调用方法之前会得到NullPointerExecption,因为该引用是在传递给供应商之前执行的。在这种情况下,控件无法达到get()方法。

List<String> underlyers=null;
System.out.println(catchException(underlyers::size));