1. Обзор
Одним из самых приятных изменений в Java 8 стало введение лямбда-выражений , поскольку они позволяют нам отказаться от анонимных классов, значительно сокращая шаблонный код и улучшая читабельность.
Ссылки на методы — это особый тип лямбда-выражений . Они часто используются для создания простых лямбда-выражений путем ссылки на существующие методы.
Существует четыре вида ссылок на методы:
- Статические методы
- Экземплярные методы конкретных объектов
- Методы экземпляра произвольного объекта определенного типа
- Конструктор
В этом руководстве мы рассмотрим ссылки на методы в Java.
2. Ссылка на статический метод
Мы начнем с очень простого примера, используя заглавные буквы и выводя список строк
:
List<String> messages = Arrays.asList("hello", "foreach", "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. Ссылка на метод экземпляра произвольного объекта определенного типа
Этот тип ссылки на метод подобен предыдущему примеру, но без необходимости создавать настраиваемый объект для выполнения сравнения.
Давайте создадим список Integer
, который мы хотим отсортировать:
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 .