8

I'm a little bit confused about Java lambdas and method references behaviour. For ex., we have this code:

import java.util.function.Consumer;

public class Main {

    private static StringBuilder sBuilder = new StringBuilder("1");

    public static void main(String[] args) {
        Consumer<String> consumer = s -> sBuilder.append(s);
        sBuilder = new StringBuilder("2");
        consumer.accept("3");
        System.out.println(sBuilder);
    }

}

Output:

23

This works as expected, but if we replace

s -> sBuilder.append(s)

with

sBuilder::append

the output will be:

2

Have you any ideas how to explain this? This isn't the same things? Thanks.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
j2esu
  • 1,647
  • 1
  • 16
  • 26
  • 1
    Is [this](http://stackoverflow.com/questions/30514995/what-is-the-difference-between-a-lambda-and-a-method-reference-at-a-runtime-leve) too convoluted for a duplicate? – Sotirios Delimanolis Jun 22 '16 at 23:27
  • @SotiriosDelimanolis a little bit :) thank you for useful references and answer – j2esu Jun 23 '16 at 20:13

1 Answers1

11

In the lambda expression, the sBuilder field is captured, but not evaluated. It will only be evaluated when the corresponding function interface method is invoked. At that point, the sBuilder references the new instance created and assigned to the field with

sBuilder = new StringBuilder("2");

In the method reference, the sBuilder field is evaluated immediately to produce a Consumer instance. That value references the instance created in the static initializer

private static StringBuilder sBuilder = new StringBuilder("1");

and the Consumer will operate on that one. You print the new one.


From the Java Language Specification, concerning the Run-Time Evaluation of Method References

The body of an invocation method depends on the form of the method reference expression, as follows:

If the form is ExpressionName :: [TypeArguments] Identifier or Primary :: [TypeArguments] Identifier, then the body of the invocation method has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

  • The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

  • The target reference is the value of ExpressionName or Primary, as determined when the method reference expression was evaluated.

  • The arguments to the method invocation expression are the formal parameters of the invocation method.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    In other words, `s -> sBuilder.append(s)` is equivalent to `s -> Main.sBuilder.append(s)`, while `sBuilder::append` is equivalent to `StringBuilder temp = Main.sBuilder; s -> temp.append(s)` – Tavian Barnes Jun 22 '16 at 23:23
  • Out of curiosity, why mark it as community wiki at first? – Jean-François Savard Jun 22 '16 at 23:25
  • 3
    @Jean-FrançoisSavard Because I'm pretty sure there's a duplicate out there but I couldn't find it. I don't care about the points. It feels wrong when I know there's more information elsewhere. – Sotirios Delimanolis Jun 22 '16 at 23:26