Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expression vs method reference implementation details

Tags:

java

lambda

Given this:

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());
    }
}
  • What are the differences in what the compiler does for each method?
  • In case there's any, is there a difference in memory usage or runtime? (even if it's small, the question is just academic)

PD: I know the question is similar to this one but I think it's not been addressed correctly.

like image 759
hithwen Avatar asked Mar 24 '15 22:03

hithwen


People also ask

How do you use method reference instead of lambda?

The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.

What are we using lambdas and method reference for?

You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name.

What are equivalent method reference for the following lambda expression?

If you are using a lambda expression as an anonymous function but not doing anything with the argument passed, you can replace lambda expression with method reference. In the first two cases, the method reference is equivalent to lambda expression that supplies the parameters of the method e.g. System.

What is the advantage of method reference in Java 8?

That's all about what is method reference is in Java 8 and how you can use it to write clean code in Java 8. The biggest benefit of the method reference or constructor reference is that they make the code even shorter by eliminating lambda expression, which makes the code more readable.


2 Answers

This is an extract from the Brian Goetz's doc linked by Brett Oken:

When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression, possibly with some additional arguments (for values captured from the lexical scope, if any.) At the point at which the lambda expression would be captured, it generates an invokedynamic call site, which, when invoked, returns an instance of the functional interface to which the lambda is being converted. This call site is called the lambda factory for a given lambda. The dynamic arguments to the lambda factory are the values captured from the lexical scope. The bootstrap method of the lambda factory is a standardized method in the Java language runtime library, called the lambda metafactory. The static bootstrap arguments capture information known about the lambda at compile time (the functional interface to which it will be converted, a method handle for the desugared lambda body, information about whether the SAM type is serializable, etc.)

Method references are treated the same way as lambda expressions, except that most method references do not need to be desugared into a new method; we can simply load a constant method handle for the referenced method and pass that to the metafactory.

Examples extracted from same doc:

As an example, consider a lambda that captures a field minSize:

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

We desugar this as an instance method, and pass the receiver as the first captured argument:

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; }

While

list.filter(String::isEmpty)

is translated as:

list.filter(indy(MH(metaFactory), MH(invokeVirtual Predicate.apply),
             MH(invokeVirtual String.isEmpty))()))
like image 76
hithwen Avatar answered Nov 10 '22 19:11

hithwen


One case where we can find the difference lambda expression & method reference is using as part of Supplier interface. Lets say we have below static method

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;
    }
}

When we call the method using lambda expression as below, code work fine since the lambda expression passed as part of Supplier & executed only when get() method is called where the exception is caught.

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

When we call the method using method reference as below, we get NullPointerExecption before calling the method since the reference is executed before passing in to the supplier. The control does not reach get() method in this case.

List<String> underlyers=null;
System.out.println(catchException(underlyers::size));
like image 43
Shree Avatar answered Nov 10 '22 21:11

Shree