Почему функциональный интерфейс инициализируется по-разному при использовании лямбда в фабричном методе и ссылке на метод (синглтон/прототип)?

У меня есть два фабричных метода, которые производят потребителей, используют разные подходы ???????? лямбда и ссылки на методы:

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

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

Я обнаружил, что в первом случае (lambdaPrintStringConsumer()) метод возвращает ссылку на тот же объект

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

но во втором (methodRefPrintStringConsumer()) объекты разные

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

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

прямой подход возвращает тот же результат, что и 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
}

, затем я протестировал фабричный метод со ссылкой на другой статический метод

public class FunctionalInterfaceTest {

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

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

    ...

}

и получим тот же результат, что и в первом тесте (lambdaPrintStringConsumer):

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

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

В чем хитрость

В тестах ???????? jdk-11.0.1 и jdk-13.0.1.


person kozmo    schedule 18.11.2019    source источник
comment
Как примечание, никогда не полагайтесь на такие внутренности. То, как Java представляет лямбда-выражения и ссылки на методы внутри, полностью зависит от Java.   -  person Zabuzard    schedule 18.11.2019
comment
настоящий вопрос заключается в том, почему это вас волнует? это детали реализации, которые могут быть изменены в любой момент времени. было бы намного проще, если бы вы могли объяснить, что вы действительно пытаетесь сделать   -  person Eugene    schedule 19.11.2019


Ответы (1)


Являются ли следующие выражения эквивалентными?

x -> System.out.println(x)

System.out::println

Нет. Если вы позвоните System.setOut, первый поднимет новый PrintStream; последний не будет.

Таким образом, в этом случае лямбда-метод не требует доступа к переменным из окружающей лексической области видимости, тогда как это выражение ссылки на метод требует. Это позволяет совместно использовать первое, но не второе.

Точные детали могут быть указаны, а могут и не быть указаны - мне не терпится посмотреть.

person Tom Hawtin - tackline    schedule 18.11.2019
comment
Том, не могли бы вы добавить больше деталей? - person kozmo; 18.11.2019
comment
@kozmo очень похоже, что ваш вопрос является дубликатом this - person Eugene; 19.11.2019
comment
@Eugene - я думаю, что мой вопрос об инициализации лямбда-выражений экземпляра не о равноправии. - person kozmo; 19.11.2019
comment
@kozmo это именно то, что вы спросили у Тома здесь: в чем разница между x -> System.out.println(x) и System.out::println - что объясняет этот ответ. - person Eugene; 19.11.2019
comment
Почему функциональный интерфейс инициализируется по-разному при использовании лямбда в фабричном методе и ссылке на метод (синглтон/прототип) - это означает, что я хочу подчеркнуть области экземпляра → синглтон или прототип - person kozmo; 19.11.2019
comment
@kozmo, чтобы понять ответ на свой вопрос, вы должны быть готовы понять предпосылки. Ссылка, которую дал вам Евгений, объясняет фундаментальные различия между этими двумя языковыми конструкциями (как и этот ответ). Как только вы поймете эти различия, вы можете использовать их, чтобы ответить на свой вопрос, например. включая ответ на вопрос "Создает ли лямбда-выражение объект в куче каждый раз, когда оно выполняется?". Чтобы дополнить этот ответ, обе конструкции могут оценивать повторно используемые объекты, но в настоящее время это делает только одна. - person Holger; 29.11.2019