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();