Запускаемый как ссылка на метод

#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; возвращает a Supplier<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);