1. Обзор
В Java 8 введено понятие ссылок на методы. Мы часто видим их похожими на лямбда-выражения.
Однако ссылки на методы и лямбда-выражения-это не совсем одно и то же. В этой статье мы покажем, почему они отличаются друг от друга и каковы риски их неправильного использования.
2. Синтаксис ссылок на лямбды и методы
Для начала давайте рассмотрим несколько примеров лямбда-выражений:
Runnable r1 = () -> "some string".toUpperCase(); Consumerc1 = x -> x.toUpperCase();
И несколько примеров ссылок на методы:
Functionf1 = String::toUpperCase; Runnable r2 = "some string"::toUpperCase; Runnable r3 = String::new;
Эти примеры могут заставить нас думать о ссылках на методы как о сокращенной нотации для лямбд.
Но давайте взглянем на официальную документацию |/Oracle . Там мы можем найти интересный пример:
(test ? list.replaceAll(String::trim) : list) :: iterator
Как мы видим, спецификация языка Java позволяет нам иметь другой вид выражений перед оператором двойного двоеточия. Часть перед :: называется целевой ссылкой .
Далее мы обсудим процесс референтной оценки метода.
3. Эталонная Оценка Метода
Что произойдет, когда мы запустим следующий код?
public static void main(String[] args) { Runnable runnable = (f("some") + f("string"))::toUpperCase; } private static String f(String string) { System.out.println(string); return string; }
Мы только что создали объект Runnable . Ни больше, ни меньше. Однако выход таков:
some string
Это произошло потому, что целевая ссылка вычисляется при первом обнаружении объявления. Следовательно, мы потеряли желанную лень. Целевая ссылка также оценивается только один раз. Итак, если мы добавим эту строку к приведенному выше примеру:
runnable.run()
Мы не увидим никакого выхода. А как насчет следующего дела?
SomeWorker worker = null; Runnable workLambda = () -> worker.work() // ok Runnable workMethodReference = worker::work; // boom! NullPointerException
Объяснение, предоставленное упомянутой ранее документацией :
“Выражение вызова метода (§15.12), вызывающее метод экземпляра, вызывает исключение NullPointerException, если целевая ссылка равна null.”
Лучший способ предотвратить непредвиденные ситуации может заключаться в том, чтобы никогда не использовать доступ к переменным и сложные выражения в качестве целевых ссылок .
Хорошей идеей может быть использование ссылок на методы только в качестве аккуратной, короткой нотации для его лямбда-эквивалента. Наличие только имени класса перед :: оператором гарантирует безопасность.
4. Заключение
В этой статье мы узнали о процессе оценки ссылок на методы.
Мы знаем риски и правила, которым мы должны следовать, чтобы не быть внезапно удивленными поведением нашего приложения.