Method References (посилання на методи) з’явилися у Java 8. Вони дозволяють передавати методи (або конструктори) як аргументи, коли очікується функціональний інтерфейс. Це альтернатива лямбдам, у яких вся робота зводиться до виклику одного методу. Посилання на метод робить код компактнішим і легшим для читання, зберігаючи ту ж саму логіку виконання.
Розглянемо приклад:
public class Main {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob", "Charlie");
// Використання lambda виразу
names.forEach(name -> System.out.println(name));
// Використання посилання на метод
names.forEach(System.out::println);
}
}У цьому прикладі:
- Метод forEach очікує на вхід функціональний інтерфейс Consumer<T> з єдиним методом accept(T t). У нашому випадку T – це String.
- Ми можемо передати лямбда-вираз name -> System.out.println(name), який приймає параметр і передає його в метод println.
- Або замінити його на посилання System.out::println. Це посилання не викликає метод одразу, а лише передає його як реалізацію інтерфейсу. Компілятор визначає, що сигнатура println(String) сумісна з методом accept(String), і виклик відбувається для кожного елемента списку.
Типи Method References у Java
У Java існує чотири основні види посилань на методи:
- посилання на статичний метод
- посилання на метод певного об’єкта
- посилання на метод будь-якого об’єкта певного типу
- посилання на конструктор
Далі буде розглянуто кожен тип на практичних прикладах.
Посилання на статичний метод
У цьому випадку використовується посилання на статичний метод класу:
public class Main {
public static void main(String[] args) {
List<String> numbers = List.of("1", "2", "3", "4", "5");
// Використання лямбда-виразу
List<Integer> result1 = numbers.stream()
.map(number -> Integer.parseInt(number))
.toList();
// Використання посилання на статичний метод
List<Integer> result2 = numbers.stream()
.map(Integer::parseInt)
.toList();
System.out.println(result1); // [1, 2, 3, 4, 5]
System.out.println(result2); // [1, 2, 3, 4, 5]
}
}У цьому прикладі Integer::parseInt є посиланням на статичний метод класу Integer, який використовується для перетворення кожного рядка у число.
Посилання на метод певного об’єкта
Цей тип використовується, коли потрібно викликати метод вже створеного об’єкта:
public class Greeter {
public void greet(String name) {
System.out.println("Hello, " + name + "!");
}
}public class Main {
public static void main(String[] args) {
Greeter greeter = new Greeter();
List<String> names = List.of("Dan", "Jen", "Isabella", "Juliana");
// Використання лямбда-виразу
names.forEach(name -> greeter.greet(name));
// Використання посилання на метод
names.forEach(greeter::greet);
}
}У цьому прикладі greeter::greet є посиланням на метод конкретного екземпляра класу Greeter, яке використовується для виклику greet для кожного елемента списку.
Посилання на метод будь-якого об’єкта певного типу
Цей тип використовується, коли потрібно викликати метод на кожному об’єкті певного типу. У такому випадку Java автоматично підставляє об’єкт, до якого застосовується метод:
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("Dan", "Jen", "Isabella", "Juliana"));
// Використання лямбда-виразу
names.sort((a, b) -> a.compareToIgnoreCase(b));
// Використання посилання на метод
names.sort(String::compareToIgnoreCase);
}
}У цьому прикладі String::compareToIgnoreCase є посиланням на метод будь-якого об’єкта типу String. Воно еквівалентне лямбда-виразу (a, b) -> a.compareToIgnoreCase(b) і використовується як реалізація функціонального інтерфейсу Comparator<String>.
Посилання на конструктор
Цей тип використовується, коли потрібно створювати нові об’єкти: замість лямбда-виразу, який викликає конструктор, можна передати посилання на нього:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{name='" + name + "'}";
}
}public class Main {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob", "Charlie");
// Використання лямбда-виразу
List<Person> people1 = names.stream()
.map(name -> new Person(name))
.toList();
// Використання посилання на конструктор
List<Person> people2 = names.stream()
.map(Person::new)
.toList();
people2.forEach(System.out::println);
}
}
/*
Output:
Person{name='Alice'}
Person{name='Bob'}
Person{name='Charlie'}
*/У цьому прикладі Person::new є посиланням на конструктор класу Person. Java автоматично передає кожен рядок з names як аргумент у конструктор Person(String name).
Підсумок
Посилання на методи – це зручний інструмент у Java, що допомагає писати чистий і зрозумілий код. Вони особливо корисні у випадках, коли лямбда-вираз просто викликає існуючий метод, роблячи запис більш компактним і наочним.