Java лямбда-метод и новый объект

У меня есть следующий код:

public class RefDemo {

    static class Demo implements Runnable {

        public Demo() {
            System.out.println(this.toString() + "-----");
        }

        @Override
        public void run() {
            System.out.println("run");
        }
    }

    public static void main(String[] args) {
        Runnable runnable = Demo::new; // lambda ref constructor method
        runnable.run(); // does not do anything
        System.out.println(runnable);
        Runnable demo = new Demo(); // using new to create a Demo instance
        demo.run();
        System.out.println(demo);
    }
}

Какие принты:

[email protected] // lambda run constructor method print
RefDemo$$Lambda$1/[email protected] // main method print
[email protected]
run
[email protected]

Я не знаю, почему код не run печать при вызове runnable.run();

Почему это происходит?

Ответы

Ответ 1

Этот код

Runnable runnable = Demo::new;

Это эквивалентно этому коду

Runnable runnable = new Runnable() {
    @Override 
    public void run() {
        new Demo();
    }
};

Таким образом, вы не имеете в виду метод run Demo а конструктор.

Ответ 2

Вы просто используете Runnable в слишком многих местах и сбиваете с толку. Следующий код немного упрощает то, что происходит:

public class RefDemo {

    static class Demo {

        public Demo() {
            System.out.println(this.toString() + "-----");
        }

        public void something() {
            System.out.println("something");
        }
    }

    public static void main(String[] args) {
        Runnable runnable = Demo::new; 
        runnable.run();

        System.out.println(runnable);

        Demo demo = new Demo();
        demo.something();

        System.out.println(demo);
    }
}

Runnable runnable = Demo::new; означает, что теперь у вас есть ссылка на конструктор Demo (который по-прежнему работает после отказа от соответствия интерфейсу Runnable). И вы сохраняете эту ссылку в переменной типа Runnable которая работает только потому, что их функциональные интерфейсы совместимы. Вызов run на этой ссылке затем просто вызывает конструктор, а не метод run/something Demo.

Ответ 3

Линии:

Runnable runnable = Demo::new;
runnable.run();

Это эквивалентно:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        new Demo(); // Creates an instance of the Demo class, and nothing more
    }
};
runnable.run();

В то же время ваше намерение состояло в том, чтобы вызвать метод run из класса Demo помощью ссылки на метод. Поэтому я предполагаю, что вы имели в виду следующее:

Runnable runnable = new Demo()::run;
runnable.run();
// But, this is a little bit redundant...

Вышеприведенный код эквивалентен:

Demo demo = new Demo();
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        demo.run();
    }
};
runnable.run();

PS Вам действительно не нужна ссылка на метод здесь, поэтому просто напишите:

new Demo().run();

Или же:

Runnable demo = new Demo();
demo.run();

Ответ 4

Demo::new - это конструктор класса Demo. Поскольку вы назначаете его ссылке на интерфейс Runnable, вызов runnable.run() вызывает конструктор и создает новый экземпляр Demo. Метод run() не выполняется.

Если вы хотите определить экземпляр Runnable который вызывает метод Demo run() используя ссылку на метод, вы можете написать:

Runnable runnable = new Demo()::run;

Конечно, поскольку Demo уже реализует Runnable, гораздо проще просто написать:

Runnable runnable = new Demo();