71

Do method references skip the overhead of the lambda wrapper? Might they in the future?

According to the Java Tutorial on Method References:

Sometimes... 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. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

I prefer the lambda syntax to the method reference syntax for several reasons:

Lambdas are clearer

Despite Oracle's claims, I find the lambda syntax easier-to-read than the object method reference short-hand because the method reference syntax is ambiguous:

Bar::foo

Are you calling a static one-argument method on the class of x and passing it x?

x -> Bar.foo(x)

Or are you calling a zero-argument instance method on x?

x -> x.foo()

The method reference syntax could stand in for either one. It hides what your code is actually doing.

Lambdas are safer

If you reference Bar::foo as a class method and Bar later adds an instance method of the same name (or vice-versa), your code will no longer compile.

You can use lambdas consistently

You can wrap any function in a lambda - so you can use the same syntax consistently everywhere. The method reference syntax won't work on methods that take or return primitive arrays, throw checked exceptions, or have the same method name used as an instance and a static method (because the method reference syntax is ambiguous about which method would be called). They don't work when you have overloaded methods with the same number of arguments, but you shouldn't do that anyway (see Josh Bloch's item 41) so we can't hold that against method references.

Conclusion

If there's no performance penalty for doing so, I'm tempted to turn off the warning in my IDE and use the lambda syntax consistently without sprinkling the occasional method reference into my code.

P.S.

Neither here nor there, but in my dreams, object method references look more like this and apply invoke-dynamic against the method directly on the object without a lambda wrapper:

_.foo()
17
  • 2
    This does not answer your question, but there are other reasons for using closures. In my opinion, many of them far outweigh the tiny overhead of an additional function call.
    – user22815
    Commented Mar 26, 2015 at 15:19
  • 10
    I think you're worrying about performance prematurely. In my opinion performance should be a secondary consideration for using a method reference; it's all about expressing your intent. If you need to pass a function somewhere, why not just pass the function instead of some other function that delegates to it? It seems strange to me; like you don't quite buy that functions are first-class things, they need an anonymous chaperone to move them around. But to address your question more directly, I wouldn't be surprised if a method reference can be implemented as a static call.
    – Doval
    Commented Mar 26, 2015 at 15:41
  • 7
    .map(_.acceptValue()): If I am not mistaken, this looks very much like the Scala syntax. Maybe you are just trying to use the wrong language.
    – Giorgio
    Commented Mar 31, 2015 at 20:37
  • 3
    "The method reference syntax won't work on methods that return void" - How so? I'm passing System.out::println to forEach() all the time...?
    – Lukas Eder
    Commented Jul 21, 2015 at 7:53
  • 3
    "The method reference syntax won't work on methods that take or return primitive arrays, throw checked exceptions...." That's not correct. You can use method references as well as lambda expressions for such cases, as long as the functional interface in question allows it. Commented Sep 14, 2015 at 21:01

3 Answers 3

18

In many scenarios, I think lambda and method-reference is equivalent. But the lambda will wrap the invocation target by the declaring interface type.

For example

public class InvokeTest {

    private static void invoke(final Runnable r) {
        r.run();
    }

    private static void target() {
        new Exception().printStackTrace();
    }

    @Test
    public void lambda() throws Exception {
        invoke(() -> target());
    }

    @Test
    public void methodReference() throws Exception {
        invoke(InvokeTest::target);
    }
}

You will see the console output the stacktrace.

In lambda(), the method calling target() is lambda$lambda$0(InvokeTest.java:20), which has traceable line info. Obviously, that is the lambda you write, the compiler generates an anonymous method for you. And then, the caller of the of the lambda method is something like InvokeTest$$Lambda$2/1617791695.run(Unknown Source), that is the invokedynamic call in JVM, it means the call is linked to the generated method.

In methodReference(), the method calling target() is directly the InvokeTest$$Lambda$1/758529971.run(Unknown Source), it means the call is directly linked to the InvokeTest::target method.

Conclusion

Above all, compare to method-reference, using lambda expression will only cause one more method call to the generating method from lambda.

1
  • 1
    The answer below about the metafactory is a bit better. I added some useful comments that are relevant to it (namely how implementations like Oracle / OpenJDK use ASM to generate the wrapper class objects needed to create lambda / method handle instances.
    – Ajax
    Commented Jul 21, 2016 at 11:08
36

It's all about the metafactory

First, most method references do not need desugaring by the lambda metafactory, they are simply used as the reference method. Under the section "Lambda body sugaring" of the Translation of Lambda Expressions ("TLE") article:

All things being equal, private methods are preferable to nonprivate, static methods preferable to instance methods, it is best if lambda bodies are desugared into in the innermost class in which the lambda expression appears, signatures should match the body signature of the lambda, extra arguments should be prepended on the front of the argument list for captured values, and would not desugar method references at all. However, there are exception cases where we may have to deviate from this baseline strategy.

This is further highlighted further down in TLE's "The Lambda Metafactory":

metaFactory(MethodHandles.Lookup caller, // provided by VM
            String invokedName,          // provided by VM
            MethodType invokedType,      // provided by VM
            MethodHandle descriptor,     // lambda descriptor
            MethodHandle impl)           // lambda body

The impl argument identifies the lambda method, either a desugared lambda body or the method named in a method reference.

A static (Integer::sum) or unbounded instance method (Integer::intValue) references are the 'simplest' or the most 'convenient', in the sense that they can be optimally handled by a 'fast-path' metafactory variant without the desugaring. This advantage is helpfully pointed out in TLE's "Metafactory variants":

By eliminating arguments where they are not needed, classfiles become smaller. And the fast path option lowers the bar for the VM to intrinsify the lambda conversion operation, enabling it to be treated as a "boxing" operation and faciliating unbox optimizations.

Naturally, an instance-capturing method reference (obj::myMethod) needs to provide the bounded instance as an argument to the method handle for invocation, which may mean the need of desugaring using 'bridge' methods.

Conclusion

I'm not exactly sure what is the lambda 'wrapper' you are hinting at, but even though the ultimate result of using your user-defined lambdas or method references are the same, the way that is reached seems to be quite different, and can be different in the future if that's not the case now. Hence, I suppose it's more likely than not that method references can be handled in a more optimal way by the metafactory.

6
  • 4
    This is the most relevant answer, as it actually touches on the internal machinery needed to create a lambda. As someone who actually debugged the lambda creation process (in order to figure out how to generate stable ids for a given lambda / method reference so they can be removed from maps), I found it quite surprising just how much work is done to create an instance of a lambda. Oracle / OpenJDK use ASM to dynamically generate classes that, if necessary, close over any variable referenced in your lambda (or the instance qualifier of a method reference)...
    – Ajax
    Commented Jul 21, 2016 at 11:10
  • 4
    ...In addition to closing over variables, lambdas in particular also create a static method that is injected into the declaring class so the created lambda has something to call to map into the function(s) you want to invoke. A method reference with an instance qualifier will take that instance as a parameter to this method; I haven't tested if a this::method reference in a private class skips this closure, but I have tested that static methods do, in fact, skip the intermediate method (and just create an object to conform to the invocation target at the call site).
    – Ajax
    Commented Jul 21, 2016 at 11:15
  • 4
    Presumably a private method also won't need virtual dispatch, whereas anything more public will need to close over the instance (via a generated static method) so that it can invokevirtual on the actual instance to make sure it notices overrides. TL;DR: Method references close over less arguments, so they leak less, and require less dynamic code generation.
    – Ajax
    Commented Jul 21, 2016 at 11:17
  • 6
    Last spammy comment... The dynamic class generation is pretty heavyweight. Still probably better than loading an anonymous class in the classloader (since the heaviest parts are only done once), but you would be really surprised just how much work goes into creating a lambda instance. It would be interesting to see real benchmarks of lambdas versus method references versus anonymous classes. Note that two lambdas or method references which reference the same things, but at different places create different classes at runtime (even within the same method, right after each other).
    – Ajax
    Commented Jul 21, 2016 at 11:20
  • 1
    @Ajax What about this one ? blog.soat.fr/2015/12/benchmark-java-lambda-vs-classe-anonyme.
    – Walfrat
    Commented Nov 4, 2016 at 15:34
0

There is one quite serious consequence when using lambda expressions that may affect performance.

When you declare a lambda expression, you are creating a closure over the local scope.

What does this mean and how does it affect performance?

Well I'm glad you asked. It means that each of those little lambda expressions is a small anonymous inner class and that means it carries with it a reference to all variables that are in the same scope of the lambda expression.

This means also this reference of the object instance and all of its fields. Depending on the timing of the actual invocation of the lambda, this can amount to a pretty significant resource leak as the garbage collector is unable to let go of these references as long as the object holding to a lambda is still alive...

4
  • Same goes for non-static method references.
    – Ajax
    Commented Jul 21, 2016 at 11:21
  • 16
    Java lambdas are not strictly closures. Nor are they anonymous inner classes. They do NOT carry a reference to all variables in scope where they are declared. They ONLY carry a reference to the variables they actually reference. This also applies to the this object which could be referenced. A lambda, therefore, does not leak resources simply by having scope for them; it only holds on to the objects it needs. On the other hand, an anonymous inner class can leak resources, but does not always do so. See this code for an example: a.blmq.us/2mmrL6v
    – squid314
    Commented Feb 24, 2017 at 23:50
  • That answer is simply wrong Commented Oct 14, 2019 at 11:39
  • Thank you @StefanReich, That was very helpful! Commented Oct 22, 2019 at 7:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.