1. Обзор
В Java 8 появилась концепция ссылок на методы. Мы часто видим их похожими на лямбда-выражения.
Однако ссылки на методы и лямбда-выражения — это не одно и то же. В этой статье мы покажем, чем они отличаются и каковы риски их неправильного использования.
2. Синтаксис лямбда-выражений и ссылок на методы
Для начала рассмотрим несколько примеров лямбда-выражений:
Runnable r1 = () -> "some string".toUpperCase();
Consumer<String> c1 = x -> x.toUpperCase();
И несколько примеров ссылок на методы:
Function<String, String> f1 = 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. Вывод
В этой статье мы узнали о процессе оценки ссылок на методы.
Мы знаем риски и правила, которым мы должны следовать, чтобы не удивляться поведению нашего приложения.