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()
GlenPeterson
  • 14,890
  • 6
  • 47
  • 75
  • 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. –  Mar 26 '15 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 Mar 26 '15 at 15:41
  • @Doval I have used lambdas as method-level adapters before. In those cases I did need an anonymous chaperone so users of the lambda did not need to know about the underlying implementation. However, I do agree that in other cases it might be overkill. –  Mar 26 '15 at 15:48
  • @Snowman and Doval, This question isn't about whether first-class functions are good or bad. It's about choosing the best Java 8 *syntax* to express them in. I've updated the question and added examples. – GlenPeterson Mar 31 '15 at 20:26
  • 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 Mar 31 '15 at 20:37
  • @GlenPeterson It's not clear; is the question about the best/most syntax between lambdas and method references or is it about which one provides more performance? If it's the first case I believe you need to update the title. – Random42 Apr 01 '15 at 07:53
  • @m3th0dman The question is based on the assumption that the lambda syntax is clearer and asks if there is a performance reason to use a method reference instead. Open-ended, opinion-based questions about "which is better" are not allowed on StackExchange. This question is specifically about performance. You are free to discuss other reasons to choose one syntax over the other in an answer, if you chose to do so, but I'm not asking that kind of question here. – GlenPeterson Apr 01 '15 at 12: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 Jul 21 '15 at 07:53
  • @LukasEder Oops - thanks! I've fixed that error. I must have been testing with a method that expected a `java.util.function.Function` instead of a `Consumer`. – GlenPeterson Jul 21 '15 at 15:15
  • 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. – Stuart Marks Sep 14 '15 at 21:01
  • "[...] a static one-argument method on the Accept class [or] a zero-argument instance method [...]?" How is that relevant? – Sebastian Redl Nov 10 '15 at 00:49
  • @SebastianRedl I tried to clear it up by editing the question. – GlenPeterson Nov 10 '15 at 14:07
  • @GlenPeterson I know what you meant. I just don't think the difference is relevant if the method has a useful name. – Sebastian Redl Nov 10 '15 at 23:37
  • 1
    I had not realized that you could refer to an instance method like it's a static method with the object as the first parameter. That kind turned me upside down for a moment to think that Java works like Python now. Well not quite but still, wow. As to your last comment, you know you can use :: against an instance right? Or are you only referring to the reference to a instance method on the class? – JimmyJames Nov 04 '16 at 19:32
  • I would list "not distinguishing between `x -> Bar.foo(x)` and `x -> x.foo()`" as a *benefit* of the method reference syntax, not a *drawback* – Caleth Nov 23 '18 at 14:40
  • 1
    @Caleth imagine a library you use has Bar.foo(x) that does one thing and later gets a method x.foo() that does something else (or vice-versa). Now all your client code using the method reference syntax breaks because it's ambiguous which you are referring to. – GlenPeterson Nov 24 '18 at 15:12

3 Answers3

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.

h.j.k.
  • 1,737
  • 1
  • 16
  • 20
  • 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 Jul 21 '16 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 Jul 21 '16 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 Jul 21 '16 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 Jul 21 '16 at 11:20
  • @Ajax What about this one ? http://blog.soat.fr/2015/12/benchmark-java-lambda-vs-classe-anonyme/. – Walfrat Nov 04 '16 at 15:34
  • We get that the ways method references and lambdas are created and used are internally different. What I don't get is why, instead of burdening the language with method references, the compiler doesn't just optimise-as-references those lambdas that are equivalent to references from the end-user's perspective. If it would have been asking too much too soon, well, then the optimisation could have been left for later on. I can't see any use for method references except as optimised alternatives to lambdas, and we're repeatedly told that kind of micro-optimisation isn't for us to care about. – entonio Nov 22 '18 at 17:17
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.

Redline
  • 103
  • 2
JasonMing
  • 296
  • 2
  • 5
  • 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 Jul 21 '16 at 11:08
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...

Roland Tepp
  • 179
  • 2
  • 9
  • Same goes for non-static method references. – Ajax Jul 21 '16 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: https://a.blmq.us/2mmrL6v – squid314 Feb 24 '17 at 23:50
  • That answer is simply wrong – Stefan Reich Oct 14 '19 at 11:39
  • Thank you @StefanReich, That was very helpful! – Roland Tepp Oct 22 '19 at 07:34