Что такое ссылки на метод в Java и виды доступных ссылок на метод?

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

Оператор ссылки на метод :: используется для ссылки на методы функционального интерфейса. Это компактная и простая форма лямбда-выражения.

Лямбда-выражения используются для создания анонимных методов.

Чаще всего мы выполняем некоторые операции внутри лямбда-выражения. Иногда, однако, лямбда-выражение не делает ничего, кроме вызова существующего метода. В этих случаях часто понятнее обращаться к существующему методу по имени.

Поэтому всякий раз, когда вы используете лямбда-выражение для обращения к методу, вы можете сделать то же самое, используя ссылку на метод. Замените лямбда-выражение на ссылку на метод, и все работает!

Ссылки на методы позволяют вам сделать это. Они представляют собой компактные, удобные для чтения лямбда-выражения для методов, у которых уже есть имя.

Ссылка на метод — это замечательная возможность, появившаяся в Java 8. Помимо использования преимуществ функционального программирования, одним из самых больших преимуществ использования ссылки на метод является то, что она минимизирует количество строк кода даже больше, чем лямбда-выражения.

Ссылки на метод — это особый тип лямбда-выражений. Они часто используются для создания простых лямбда-выражений путем ссылки на существующие методы.

Пример 01: Строки в верхнем регистре

import java.util.Arrays;
import java.util.List;

class Main {
  public static void main(String[] args) {
    List<String> fruits = 
      Arrays.asList("Apple", "Banana", "guava", "grapes");

    fruits.stream()
      .map(String::toUpperCase)
      .forEach(System.out::println);
    }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Мы могли бы использовать лямбда-выражение внутри терминальной операции .forEach(...), как показано ниже.

fruits.stream()
  .map(String::toUpperCase)
  .forEach(fruit -> System.out.println(fruit));
Войти в полноэкранный режим Выйти из полноэкранного режима

Но синтаксис ссылок на методы чист и прост. Приведенный выше синтаксис лямбда-выражения рефакторингован с помощью ссылки на метод следующим образом.

    .forEach(System.out::println);
Вход в полноэкранный режим Выход из полноэкранного режима

Пояснение

Давайте подробно рассмотрим, чего мы пытаемся достичь в приведенном выше фрагменте.

  1. У нас есть список фруктов.

  2. В результате цепочки .stream() коллекция объектов String преобразуется в Stream объектов String.

  3. Теперь нам нужно связать .stream() с промежуточными операциями, такими как .map(...), которая использует ссылку на метод, String::toUpperCase для перевода всех элементов потока в верхний регистр.

  4. Наконец, мы связали его с терминальной операцией .forEach(...), которая также использует ссылку на метод для печати данных.

Чтобы понять, что означают промежуточные и терминальные операции в API потоков, прочтите эти статьи hashnode для лучшего понимания.

  • Введение в Java Streams API.

  • Как работает API потоков? Глубокое погружение в поток потоковых операций.

От лямбда-выражений к ссылкам на методы

Вот несколько примеров того, как мы можем заменить лямбда-выражения ссылками на методы.

Далее мы рассмотрим различные виды ссылок на методы, их использование, пример фрагмента кода и объяснение.

Виды ссылок на методы

Существует четыре вида ссылок на методы.

  1. Ссылки на статические методы.

  2. Ссылка на методы экземпляров конкретных объектов.

  3. Ссылка на метод экземпляра произвольного объекта определенного типа.

  4. Ссылка на конструктор.

Общий синтаксис

Итак, общий синтаксис для всех этих видов ссылок на методы выглядит следующим образом.

class/object::method
Войти в полноэкранный режим Выход из полноэкранного режима

1. Ссылка на статический метод

Ссылка на статический метод относится к статическому методу класса. Мы можем использовать ссылку на метод для прямого вызова статических методов. Синтаксис ссылки на статический метод выглядит следующим образом.

Синтаксис

Это классический синтаксис, в котором за именем класса следует статический метод, на который вы пытаетесь сослаться.

className::staticMethodName
Вход в полноэкранный режим Выйти из полноэкранного режима

Код

Пример фрагмента кода, который объясняет, как вызываются статические методы с помощью ссылок на методы.

Book POJO с конструктором, геттерами и сеттерами.

class Book {
  String title;
  String author;
  Integer year;
  Integer copiesSoldInMillions;
  Double rating;
  Double costInEuros;

  public Book(String title, String author, Integer year, Integer copiesSoldInMillions, Double rating, Double costInEuros) {
    this.title = title;
    this.author = author;
    this.year = year;
    this.copiesSoldInMillions = copiesSoldInMillions;
    this.rating = rating;
    this.costInEuros = costInEuros;
  }

  public String getTitle() {
    return title;
  }

  public Double getRating() {
    return rating;
  }

  @Override
  public String toString() {
    return "Book{" +
      "title='" + title + ''' +
      ", author='" + author + ''' +
      ", year=" + year +
      ", copiesSoldInMillions=" + copiesSoldInMillions +
      ", rating=" + rating +
      ", costInEuros=" + costInEuros +
      '}';
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

BookDatabase для внедрения фиктивных данных.

import java.util.Arrays;
import java.util.List;

public class BookDatabase {
  public static List<Book> getAllBooks() {
    return Arrays.asList(
      new Book("Don Quixote", "Miguel de Cervantes", 1605, 500, 3.9, 9.99),
      new Book("A Tale of Two Cities", "Charles Dickens", 1859, 200, 3.9, 10.0),
      new Book("The Lord of the Rings", "J.R.R. Tolkien", 2001, 150, 4.0, 12.50),
      new Book("The Little Prince", "Antoine de Saint-Exupery", 2016, 142, 4.4, 5.0),
      new Book("The Dream of the Red Chamber", "Cao Xueqin", 1791, 100, 4.2, 10.0)
    );
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Далее следует класс BookApplication, который выполняет императивное программирование или мутации переменной book внутри цикла for-loop с использованием ссылки на статический метод.

import java.util.List;

public class BookApplication {

  public static int compareByTitle(Book first, Book second) {
    return first.getTitle().compareTo(second.getTitle());
  }

  public static int compareByRating(Book first, Book second) {
    return first.getRating().compareTo(second.getRating());
  }

  public static void main(String[] args) {
    List<Book> books = BookDatabase.getAllBooks();

    System.out.println("SORT BASED ON RATINGS: ");
    books.sort(BookApplication::compareByRating);
    books.stream()
      .map(book -> book.getTitle() + " -> " + book.getRating())
      .forEach(System.out::println);

    System.out.println("---------");

    System.out.println("SORT BASED ON TITLES: ");
    books.sort(BookApplication::compareByTitle);
    books.stream()
      .map(book -> book.getTitle() + " -> " + book.getRating())
      .forEach(System.out::println);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вывод:

Приведенный выше код, фрагмент выводит на консоль следующее.

SORT BASED ON RATINGS: 
Don Quixote -> 3.9
A Tale of Two Cities -> 3.9
The Lord of the Rings -> 4.0
The Dream of the Red Chamber -> 4.2
The Little Prince -> 4.4
---------
SORT BASED ON TITLES: 
A Tale of Two Cities -> 3.9
Don Quixote -> 3.9
The Dream of the Red Chamber -> 4.2
The Little Prince -> 4.4
The Lord of the Rings -> 4.0
Вход в полноэкранный режим Выйти из полноэкранного режима

2. Ссылка на метод экземпляра конкретного объекта

Ниже приведен пример ссылки на метод экземпляра конкретного объекта:

Синтаксис:

Это другой синтаксис, в котором используются экземпляры конкретного объекта, за которыми следует статический метод, на который вы пытаетесь сослаться.

object::staticMethodName
Вход в полноэкранный режим Выход из полноэкранного режима

Код

Пример фрагмента кода, который объясняет, как вызываются статические методы с помощью ссылок на методы.

Для простоты я не буду дублировать здесь классы Book и BookDatabase, на них я ссылался в приведенном выше примере.

Итак, перейдем непосредственно к классу BookApplication.

import java.util.List;

public class BookApplication {

  public static void main(String[] args) {
    List<Book> books = BookDatabase.getAllBooks();

    BookApplication bookApplication = new BookApplication();

    System.out.println("SORT BASED ON RATINGS");
    books.sort(bookApplication::compareByRating);
    books.stream()
      .map(book -> book.getTitle() + " -> " + book.getRating())
      .forEach(System.out::println);

    System.out.println();

    System.out.println("SORT BASED ON TITLES: ");
    books.sort(bookApplication::compareByTitle);
    books.stream()
      .map(book -> book.getTitle() + " -> " + book.getRating())
      .forEach(System.out::println);
  }

  public int compareByTitle(Book first, Book second) {
    return first.getTitle().compareTo(second.getTitle());
  }

  public int compareByRating(Book first, Book second) {
    return first.getRating().compareTo(second.getRating());
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вывод:

Приведенный выше код, сниппет выводит на консоль следующее.

SORT BASED ON RATINGS
Don Quixote -> 3.9
A Tale of Two Cities -> 3.9
The Lord of the Rings -> 4.0
The Dream of the Red Chamber -> 4.2
The Little Prince -> 4.4

SORT BASED ON TITLES: 
A Tale of Two Cities -> 3.9
Don Quixote -> 3.9
The Dream of the Red Chamber -> 4.2
The Little Prince -> 4.4
The Lord of the Rings -> 4.0
Вход в полноэкранный режим Выход из полноэкранного режима

Ссылка на метод bookApplication::compareByRating вызывает метод compareByRating, который является частью объекта bookApplication. JRE определяет тип аргументов метода, которыми в данном случае являются (Book book).

Приведенное выше простое объяснение является таким же и для этой ссылки метода bookApplication::compareByTitle.

3. Ссылка на метод экземпляра произвольного объекта определенного типа.

Ниже приведен пример ссылки на метод экземпляра произвольного объекта определенного типа.

Код

Метод 01
import java.util.Arrays;
import java.util.List;

class Main {
  public static void main(String[] args) {
    List<String> fruits = 
        Arrays.asList("Banana", "Grapes", "guava", "apples");
    fruits.sort(String::compareToIgnoreCase);
    fruits.forEach(System.out::println);
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима
Выход:
apples
Banana
Grapes
guava
Войти в полноэкранный режим Выйти из полноэкранного режима

Эквивалентное лямбда-выражение для ссылки на метод String::compareToIgnoreCase будет иметь формальный список параметров (String a, String b), где a и b — произвольные имена, используемые для лучшего описания данного примера. Ссылка на метод вызовет метод a.compareToIgnoreCase(b).

Аналогично, ссылка на метод String::concat вызовет метод a.concat(b).

Подход 02
import java.util.Arrays;
import java.util.List;

class Main {
  public static void main(String[] args) {
    List<Integer> numbers = 
      Arrays.asList(11, 4, 2, 8, 9, 10, 32, 22, 20, 17);

    numbers.stream()
      // .sorted((a, b) -> a.compareTo(b)) lambda way
        .sorted(Integer::compareTo) 
        .forEach(s -> System.out.print(s + " "));
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима
Выход:
2 4 8 9 10 11 17 20 22 32
Войти в полноэкранный режим Выход из полноэкранного режима

4. Ссылка на конструктор

Ссылки на конструктор — это специализированные формы ссылок на методы, которые ссылаются на конструкторы класса. Они могут быть созданы с помощью className и ключевого слова new.

Синтаксис

className::new
Войти в полноэкранный режим Выйти из полноэкранного режима

Код

public class BookApplication {
  public static void main(String[] args) {
    BookService bookService = Book::new;
    Book book = bookService.getBook(
      "The Little Prince",
      "Antoine de Saint-Exupery",
      2016, 142,
      4.4,
      5.0);

    System.out.println(book);
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Выход:

Book{title='The Little Prince', author='Antoine de Saint-Exupery', year=2016, copiesSoldInMillions=142, rating=4.4, costInEuros=5.0}
Вход в полноэкранный режим Выход из полноэкранного режима

Вот и все, что вам нужно знать о ссылках на методы в Java. Счастливого кодинга ?.

Читать далее

  1. Введение в Java Streams API

  2. Как работает API потоков? Глубокое погружение в поток потоковых операций

  3. Функциональное программирование и парадигмы программирования в Java

  4. Императивный и декларативный стили программирования

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *