«1. Обзор
Одним из самых приятных изменений в Java 8 было введение лямбда-выражений, поскольку они позволяют нам отказаться от анонимных классов, значительно сокращая шаблонный код и улучшая читабельность.
Ссылки на методы — это особый тип лямбда-выражений. Они часто используются для создания простых лямбда-выражений путем ссылки на существующие методы.
Существует четыре типа ссылок на методы:
-
Статические методы Методы экземпляров определенных объектов Методы экземпляров произвольного объекта определенного типа Конструктор
В этом руководстве мы рассмотрим ссылки на методы в Java.
2. Ссылка на статический метод
Мы начнем с очень простого примера, набрав заглавные буквы и напечатав список строк:
List<String> messages = Arrays.asList("hello", "baeldung", "readers!");
Мы можем добиться этого, используя простое лямбда-выражение, вызывающее StringUtils .capitalize() напрямую:
messages.forEach(word -> StringUtils.capitalize(word));
Или мы можем использовать ссылку на метод, чтобы просто обратиться к статическому методу Capitalize:
messages.forEach(StringUtils::capitalize);
Обратите внимание, что ссылки на методы всегда используют оператор ::.
3. Ссылка на метод экземпляра конкретного объекта
Чтобы продемонстрировать этот тип ссылки на метод, рассмотрим два класса:
public class Bicycle {
private String brand;
private Integer frameSize;
// standard constructor, getters and setters
}
public class BicycleComparator implements Comparator {
@Override
public int compare(Bicycle a, Bicycle b) {
return a.getFrameSize().compareTo(b.getFrameSize());
}
}
И создадим объект BicycleComparator для сравнения размеров велосипедных рам: ~ ~~
BicycleComparator bikeFrameSizeComparator = new BicycleComparator();
Мы могли бы использовать лямбда-выражение для сортировки велосипедов по размеру рамы, но нам нужно указать два велосипеда для сравнения:
createBicyclesList().stream()
.sorted((a, b) -> bikeFrameSizeComparator.compare(a, b));
Вместо этого мы можем использовать ссылку на метод, чтобы получить параметр дескриптора компилятора для нас:
createBicyclesList().stream()
.sorted(bikeFrameSizeComparator::compare);
Ссылка на метод намного чище и читабельнее, так как наше намерение ясно показано в коде.
4. Ссылка на метод экземпляра произвольного объекта определенного типа
Этот тип ссылки на метод аналогичен предыдущему примеру, но без необходимости создавать пользовательский объект для выполнения сравнения.
Давайте создадим список целых чисел, который мы хотим отсортировать:
List<Integer> numbers = Arrays.asList(5, 3, 50, 24, 40, 2, 9, 18);
Если мы используем классическое лямбда-выражение, оба параметра должны быть переданы явно, тогда как использование ссылки на метод намного проще:
numbers.stream()
.sorted((a, b) -> a.compareTo(b));
numbers.stream()
.sorted(Integer::compareTo);
~ ~~ Несмотря на то, что это по-прежнему однострочник, справочник по методам намного легче читать и понимать.
5. Ссылка на конструктор
Мы можем ссылаться на конструктор так же, как ссылались на статический метод в нашем первом примере. Единственное отличие состоит в том, что мы будем использовать новое ключевое слово.
Давайте создадим массив Bicycle из списка String с разными брендами:
List<String> bikeBrands = Arrays.asList("Giant", "Scott", "Trek", "GT");
Сначала мы добавим новый конструктор в наш класс Bicycle:
public Bicycle(String brand) {
this.brand = brand;
this.frameSize = 0;
}
Далее мы будем использовать наш новый конструктор из ссылки на метод и создать массив Bicycle из исходного списка String:
bikeBrands.stream()
.map(Bicycle::new)
.toArray(Bicycle[]::new);
Обратите внимание, как мы вызывали конструкторы Bicycle и Array, используя ссылку на метод, что придает нашему коду гораздо более лаконичный и ясный вид.
6. Дополнительные примеры и ограничения
Как мы уже видели, ссылки на методы — отличный способ сделать наш код и намерения очень ясными и читабельными. Однако мы не можем использовать их для замены всех видов лямбда-выражений, поскольку они имеют некоторые ограничения.
Их основное ограничение является результатом того, что также является их самой большой силой: выходные данные предыдущего выражения должны соответствовать входным параметрам сигнатуры метода, на который ссылаются.
Давайте рассмотрим пример этого ограничения:
createBicyclesList().forEach(b -> System.out.printf(
"Bike brand is '%s' and frame size is '%d'%n",
b.getBrand(),
b.getFrameSize()));
Этот простой случай не может быть выражен ссылкой на метод, потому что в нашем случае метод printf требует 3 параметра и использует createBicyclesList().forEach() позволит только ссылке на метод вывести один параметр (объект Bicycle).
Наконец, давайте рассмотрим, как создать недействующую функцию, на которую можно ссылаться из лямбда-выражения.
В этом случае мы хотим использовать лямбда-выражение без использования его параметров.
Во-первых, давайте создадим метод doNothingAtAll:
private static <T> void doNothingAtAll(Object... o) {
}
Поскольку это метод с переменными аргументами, он будет работать в любом лямбда-выражении, независимо от объекта, на который делается ссылка, или количества выводимых параметров.
Теперь давайте посмотрим на это в действии:
createBicyclesList()
.forEach((o) -> MethodReferenceExamples.doNothingAtAll(o));
7. Заключение
«В этом кратком руководстве мы узнали, что такое ссылки на методы в Java и как их использовать для замены лямбда-выражений, тем самым улучшая читаемость и проясняя намерения программиста.
Весь код, представленный в этой статье, доступен на GitHub.