Перейти к основному содержимому

Ссылки на методы в Java

· 4 мин. чтения

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 .