Lambda expression vs method reference

JavaIntellij IdeaLambdaJava 8Side Effects

Java Problem Overview


IntelliJ keeps proposing me to replace my lambda expressions with method references.

Is there any objective difference between both of them?

Java Solutions


Solution 1 - Java

Let me offer some perspective on why we added this feature to the language, when clearly we didn't strictly need to (all methods refs can be expressed as lambdas.)

Note that there is no right answer. Anyone who says "always use a method ref instead of a lambda" or "always use a lambda instead of a method ref" should be ignored.

This question is very similar in spirit to "when should I use a named class vs an anonymous class"? And the answer is the same: when you find it more readable. There are certainly cases that are definitely one or definitely the other but there's a host of grey in the middle, and judgment must be used.

The theory behind method refs is simple: names matter. If a method has a name, then referring to it by name, rather than by an imperative bag of code that ultimately just turns around and invokes it, is often (but not always!) more clear and readable.

The arguments about performance or about counting characters are mostly red herrings, and you should ignore them. The goal is writing code that is crystal clear what it does. Very often (but not always!) method refs win on this metric, so we included them as an option, to be used in those cases.

A key consideration about whether method refs clarify or obfuscate intent is whether it is obvious from context what is the shape of the function being represented. In some cases (e.g., map(Person::getLastName), it's quite clear from the context that a function that maps one thing to another is required, and in cases like this, method references shine. In others, using a method ref requires the reader to wonder about what kind of function is being described; this is a warning sign that a lambda might be more readable, even if it is longer.

Finally, what we've found is that most people at first steer away from method refs because they feel even newer and weirder than lambdas, and so initially find them "less readable", but over time, when they get used to the syntax, generally change their behavior and gravitate towards method references when they can. So be aware that your own subjective initial "less readable" reaction almost certainly entails some aspect of familiarity bias, and you should give yourself a chance to get comfortable with both before rendering a stylistic opinion.

Solution 2 - Java

Long lambda expressions consisting of several statements may reduce the readability of your code. In such a case, extracting those statements in a method and referencing it may be a better choice.

The other reason may be re-usability. Instead of copy&pasting your lambda expression of few statements, you can construct a method and call it from different places of your code.

Solution 3 - Java

As user stuchl4n3k wrote in comments to question there may exception occurs.

Lets consider that some variable field is uninitialized field, then:

field = null;
runThisLater(()->field.method());
field = new SomeObject();

will not crash, while

field = null;
runThisLater(field::method);
field = new SomeObject();

will crash with java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()', at a method reference statement line, at least on Android.

Todays IntelliJ notes "may change semantics" while suggesting this refactoring.

This happens when do "referencing" of instance method of a particular object. Why? Lets check first two paragraphs of 15.13.3. Run-Time Evaluation of Method References:

> At run time, evaluation of a method reference expression is similar to evaluation of a > class instance creation expression, insofar as normal completion produces a reference > to an object. Evaluation of a method reference expression is distinct from invocation > of the method itself. > > First, if the method reference expression begins with an ExpressionName or a > Primary, this subexpression is evaluated. If the subexpression evaluates to null, a > NullPointerException is raised, and the method reference expression completes > abruptly. If the subexpression completes abruptly, the method reference expression > completes abruptly for the same reason.

In case of lambda expression, I'm unsure, final type is derived in compile-time from method declaration. This is just simplification of what is going exactly. But lets assume that method runThisLater has been declared as e.g. void runThisLater(SamType obj), where SamType is some Functional interface then runThisLater(()->field.method()); translates into something like:

runThisLater(new SamType() {  
    void doSomething() { 
        field.method();
    }
});

Additional info:

Solution 4 - Java

While it is true that all methods references can be expressed as lambdas, there is a potential difference in semantics when side effects are involved. @areacode's example throwing an NPE in one case but not in the other is very explicit regarding the involved side effect. However, there is a more subtle case you could run into when working with CompletableFuture:

Let's simulate a task that takes a while (2 seconds) to complete via the following helper function slow:

private static <T> Supplier<T> slow(T s) {
    return () -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {}
        return s;
    };
}

Then

var result =
    CompletableFuture.supplyAsync(slow(Function.identity()))
        .thenCompose(supplyAsync(slow("foo"))::thenApply);

Effectively runs both async tasks in parallel allowing the future to complete after roughly 2 seconds.

On the other hand if we refactor the ::thenApply method reference into a lambda, both async tasks would run sequentially one after each other and the future only completes after about 4 seconds.

Side note: while the example seems contrived, it does come up when you try to regain the applicative instance hidden in the future.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGerardView Question on Stackoverflow
Solution 1 - JavaBrian GoetzView Answer on Stackoverflow
Solution 2 - JavaovunccetinView Answer on Stackoverflow
Solution 3 - JavaaeracodeView Answer on Stackoverflow
Solution 4 - JavamichidView Answer on Stackoverflow