7

I have two factory-methods which produce "consumers" use different approaches lambda and method references:

@SuppressWarnings("Convert2MethodRef")
public Consumer<String> lambdaPrintStringConsumer(){
    return x -> System.out.println(x);
}

public Consumer<String> methodRefPrintStringConsumer(){
    return System.out::println;
}

I found that in first case (lambdaPrintStringConsumer()), method return reference to the same object

@Test
public void shouldSameFromFactoryMethod_lambda() {
    Consumer<String> consumerA = lambdaPrintStringConsumer();
    Consumer<String> consumerB = lambdaPrintStringConsumer();
    
    Assert.assertSame(consumerA, consumerB);//consumerA == consumerB --> true
}

but in the second (methodRefPrintStringConsumer()), objects is different

@Test
public void shouldNotSameFromFactoryMethod_methodRef() {
    Consumer<String> consumerA = methodRefPrintStringConsumer();
    Consumer<String> consumerB = methodRefPrintStringConsumer();

    Assert.assertNotSame(consumerA, consumerB);//consumerA == consumerB --> false
}

direct approach return the same result as shouldNotSameFromFactoryMethod_methodRef():

@SuppressWarnings("Convert2MethodRef")
@Test
public void shouldNotSameFromLambda() {
    Consumer<String> consumerA = s -> System.out.println(s);
    Consumer<String> consumerB = s -> System.out.println(s);

    Assert.assertNotSame(consumerA, consumerB);//consumerA == consumerB --> false
}

, next I tested factory-method with method reference to other static method

public class FunctionalInterfaceTest {

    public static Consumer<String> methodRefFromStaticMethodStringConsumer() {
        return FunctionalInterfaceTest::print;
    }

    public static void print(String string) {
        System.out.println(string);
    }

    ...

}

and get the same result as in the first test (lambdaPrintStringConsumer):

@Test
public void shouldSameFromFactoryMethod_methodRef() {
    Consumer<String> consumerA = methodRefFromStaticMethodStringConsumer();
    Consumer<String> consumerB = methodRefFromStaticMethodStringConsumer();

    Assert.assertSame(consumerA, consumerB );//consumerA == consumerB --> true
}

What is the trick

In Tests jdk-11.0.1 and jdk-13.0.1.

kozmo
  • 4,024
  • 3
  • 30
  • 48
  • 11
    Just as a note, never rely on such internals. It is completely up to Java how it represents lambdas and method references internally. – Zabuzard Nov 18 '19 at 17:46
  • 2
    the real question is _why_ do you care? these are implementation details and are subject to change at any point in time. it would be much easier if you could explain what are you really trying to do – Eugene Nov 19 '19 at 14:47

1 Answers1

5

Are the following expressions equivalent?

x -> System.out.println(x)

System.out::println

No. If you call System.setOut, the former will pick up the new PrintStream; the latter will not.

So, in this case, the lambda method does not require access to variables from the enclosing lexical scope, whereas this method reference expression does. This allow the former to be shared but the latter cannot.

The exact details may or may not be specified - I can't be bothered to look.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Tom, could you add more details? – kozmo Nov 18 '19 at 19:15
  • 2
    @kozmo it very much looks like your question is a duplicate of [this](https://stackoverflow.com/questions/28023364/what-is-the-equivalent-lambda-expression-for-system-outprintln) – Eugene Nov 19 '19 at 14:07
  • @Eugene - I think, my question about initialuzation of instance's lambda expressions, nit about equiality. – kozmo Nov 19 '19 at 14:35
  • @kozmo this is _exactly_ what you have asked Tom here: what is the diff between `x -> System.out.println(x)` and `System.out::println` - which that answer explains. – Eugene Nov 19 '19 at 14:39
  • "Why Functional interface initialize different when use lambda in factory-method and method reference (singleton / prototype)" - It means, i want to underline scopes of instance -> singleton or prototipe – kozmo Nov 19 '19 at 14:53
  • 2
    @kozmo to understand the answer to your question, you must be willing to understand the prerequisites. The link, Eugene gave you, explains the fundamental differences between these two language constructs (just like this answer). Once you understood these differences, you can use them to answer your question, e.g. incorporating [the answer to “Does a lambda expression create an object on the heap every time it's executed?”](https://stackoverflow.com/a/27524543/2711488). Just to complement this answer, both constructs are allowed to evaluate to reused objects, but currently, only one does. – Holger Nov 29 '19 at 17:21