#java #lambda #java-8 #method-reference
#java #лямбда #java-8 #метод-эталонный
Вопрос:
Почему этот код не компилируется? Невозможно полностью понять нюансы ссылки на метод java: (
public class TestClass {
static void println() {}
public static void main(String[] args) {
Runnable r1 = () -> System.out::println; // compilation error
Runnable r2 = () -> TestClass::println; // compilation error
Runnable r2 = () -> System.out.println("Hello World"); // This is fine !!
}
}
Комментарии:
1. о чем вам сообщают сообщения об ошибках? вы говорите «печатать», но не говорите, что и как печатать
2. Представьте это как «X.y» будет обращаться к (выполнять) методу
y
X
, в то времяX::y
как ссылается на (копирует) выполнение методаy
X
.
Ответ №1:
Здесь есть небольшое недопонимание, и другие ответы дают только конечный результат. Итак, вот реальное объяснение.
В Java есть лямбды ( () -> something
) и ссылки на методы ( Class::method
). Это в основном одно и то же, просто с другим (более коротким) синтаксисом. Вы можете смешивать лямбды и ссылки на методы, если хотите (лямбда в лямбде, ссылка на метод в лямбде). Пока все имеет правильный тип, все в порядке.
So System.out::println
— это ссылка на метод типа Consumer<String>
, причина System.out.println()
принимает String
в качестве аргумента и возвращает void
— это Consumer<String>
. (Существует также System.out.println()
, который не принимает аргументов, в этом случае это было бы Runnable
)
Runnable
немного отличается, потому что он не получает никаких аргументов или возвращает значение. Примером может быть List::clear
.
Что касается того, почему ваш код был ошибкой компиляции:
При объявлении a Runnable
как лямбда-выражения вы не должны получать никаких аргументов и возвращать void
. Это лямбда-выражение: Runnable r1 = () -> System.out::println;
не соответствует критериям, потому что оно не получает аргументов ( () ->
) , но его выражение возвращает тип — Consumer<String>
(или Runnable
снова), а не a void
. Это может быть действительным Runnable
, если вам удалось ничего не вернуть, например
Runnable r1 = () -> {
Consumer<String> str1 = System.out::println; // either this
Runnable str2 = System.out::println; // or this
return; // return type - void
}
Но это как-то не имеет смысла.
Таким образом, вы можете ясно видеть, что () -> System.out::println()
на самом деле это лямбда-выражение, которое поставляет Runnable
или Consumer<String>
, поэтому его тип должен быть
Supplier<Runnable> s = () -> System.out::println; // either this
Supplier<Consumer<String>> s = () -> System.out::println; // or this
Чтобы использовать его напрямую, вам просто нужно отказаться от Supplier<>
Runnable s = System.out::println; // either this
Consumer<String> s = System.out::println; // or this
Как насчет того, чтобы просто объяснить, что вы пытаетесь сделать, и мы вам поможем.
Комментарии:
1. спасибо, что приложили усилия, чтобы рассказать так подробно. Это проясняет ситуацию для меня. Я допустил ошибку, смешав выражение lamba со ссылкой на метод, не понимая, как ссылка на метод будет интерпретироваться именно в этот момент. Ссылка на метод в моем случае возвращала поставщика, в то время как у меня сложилось впечатление, что он будет выполняться и возвращать void.
Ответ №2:
Это должна быть «прямая» ссылка на метод, а не поставщик чего-либо, представленного ссылкой на метод.
Runnable r1 = System.out::println;
Runnable r2 = TestClass::println;
Сравнить
Supplier<Runnable> a = () -> System.out::println;
с
Runnable b = System.out::println;
Комментарии:
1. Спасибо, я понимаю свою глупость
Ответ №3:
Ссылки на методы работают не так. Вы используете их следующим образом:
Runnable r1 = System.out::println;
Комментарии:
1. Даже я ошибаюсь или приведенное выше предложение неверно. На самом деле
() -> System.out::println;
возвращает aSupplier<Runnable>
и работает точно так же, как работают ссылки на методы…2. Спасибо, я понимаю свою глупость
Ответ №4:
Потому что вам нужно писать таким образом:
Runnable r1 = System.out::println;
Runnable r2 = TestClass::println;
Здесь вы можете прочитать больше о ссылках на методы https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Комментарии:
1. Спасибо, я понимаю свою глупость
Ответ №5:
В этом случае System.out.println является потребителем (он принимает строку и что-то с ней делает)
Consumer<String> c = System.out::println;
List<String> strings = Arrays.asList("Whee", "Boo");
strings.forEach(c); <-- We then pass this consumer here.
или без:
List<String> strings = Arrays.asList("Whee", "Boo");
strings.forEach(element -> System.out.println(element)); <-- without method reference
strings.forEach(System.out::println);