Ссылки на методы
Часто лямбда лишь вызывает один готовый метод и передаёт ему свой параметр: s -> s.toUpperCase(), x -> IO.println(x). Такую лямбду можно записать короче — ссылкой на метод через оператор ::.
Первая ссылка на метод
Слева от :: указывают класс или объект, справа — имя метода без скобок. Обе строки ниже делают одно и то же:
void main() {
List<String> names = List.of("Анна", "Олег");
names.forEach(name -> IO.println(name));
names.forEach(IO::println);
}Результат:
Анна
Олег
Анна
ОлегСсылка IO::println значит «вызови IO.println() с каждым элементом». Аргументы подставит сама Java. Ссылка на метод — не вызов: метод выполнится позже, когда его вызовет forEach().
Статический метод
Ссылка вида Класс::статическийМетод передаёт параметры лямбды в статический метод. Так удобно превращать строки в числа:
void main() {
Function<String, Integer> parse = Integer::parseInt;
BinaryOperator<Integer> add = Integer::sum;
IO.println(parse.apply("42") + 1);
IO.println(add.apply(2, 3));
}Результат:
43
5Метод произвольного объекта
Запись String::toUpperCase похожа на статическую, но работает иначе: первый параметр лямбды становится объектом, у которого вызывается метод. Это эквивалент s -> s.toUpperCase():
void main() {
Function<String, String> upper = String::toUpperCase;
Function<String, Integer> length = String::length;
IO.println(upper.apply("привет"));
IO.println(length.apply("привет"));
}Результат:
ПРИВЕТ
6Именно так устроены компараторы из главы о коллекциях: Comparator.comparing(Product::price) вызывает price() у каждого товара.
Метод конкретного объекта
Если слева стоит переменная, метод будет вызываться у этого объекта, а параметр лямбды станет аргументом:
void main() {
List<String> cart = new ArrayList<>();
Consumer<String> addToCart = cart::add;
addToCart.accept("чай");
addToCart.accept("печенье");
IO.println(cart);
}Результат:
[чай, печенье]Ссылка на конструктор
Класс::new — ссылка на конструктор. Она создаёт новый объект из параметров лямбды:
record User(String name) {}
void main() {
Function<String, User> create = User::new;
Supplier<List<String>> newList = ArrayList::new;
IO.println(create.apply("Анна"));
IO.println(newList.get());
}Результат:
User[name=Анна]
[]Частая ошибка
Ссылка на метод подходит, только если параметры и тип результата метода совпадают с функциональным интерфейсом. Например, String::length возвращает число, а Predicate ждёт boolean:
void main() {
Predicate<String> check = String::length;
}Результат:
Main.java:2: error: incompatible types: bad return type in method reference
Predicate<String> check = String::length;
^
int cannot be converted to booleanЕсли метод не подходит напрямую, напишите обычную лямбду: s -> s.length() > 3. Вообще ссылка на метод — не обязанность, а сокращение: выбирайте ту запись, которая читается легче.