1. Вступ
Method reference (або українською — посилання на метод) — це спеціальний синтаксис у Java, що дає змогу передавати вже наявний метод (або конструктор) як реалізацію функціонального інтерфейсу. Метод можна «передати» туди, де очікується лямбда-вираз, якщо сигнатури збігаються.
Синтаксис:
Клас::метод
об’єкт::метод
Клас::new
Якщо лямбда — це мініфункція «на льоту», то method reference — це просто «передати вже наявний метод». Це схоже на ситуацію, коли замість переписування рецепта ви даєте посилання на сторінку з рецептом.
Приклад «на пальцях»
Замість цього:
list.forEach(s -> System.out.println(s));
Можна так:
list.forEach(System.out::println);
Виглядає лаконічно, чи не так?
2. Види посилань на методи
У посилань на методи є чотири основні форми. Ось вони — цього цілком достатньо.
Посилання на статичний метод
Синтаксис: Клас::статичнийМетод
Приклад:
Function<Integer, String> intToString = String::valueOf;
System.out.println(intToString.apply(123)); // "123"
Те саме — через лямбду:
Function<Integer, String> intToString = i -> String.valueOf(i);
Посилання на нестатичний метод об’єкта
Синтаксис: об’єкт::метод
Приклад:
PrintStream printer = System.out;
Consumer<String> consumer = printer::println;
consumer.accept("Привіт, світе!");
Еквівалентно лямбді:
Consumer<String> consumer = s -> printer.println(s);
Посилання на нестатичний метод класу
Синтаксис: Клас::метод
Тут перший параметр функціонального інтерфейсу стає об’єктом, на якому викликається метод.
Приклад:
Function<String, Integer> stringLength = String::length;
System.out.println(stringLength.apply("Java")); // 4
Тут String::length перетворюється на функцію: (String s) -> s.length()
Посилання на конструктор
Синтаксис: Клас::new
Приклад:
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
ArrayList<String> list = listSupplier.get();
Еквівалентно лямбді:
Supplier<ArrayList<String>> listSupplier = () -> new ArrayList<>();
3. Коли використовувати method reference?
Method reference доречно застосовувати, коли лямбда лише викликає наявний метод без додаткової логіки — так код стає коротшим і читабельнішим.
Приклад: сортування списку
Замість цього:
List<String> names = Arrays.asList("Іван", "Петро", "Анна");
names.sort((a, b) -> a.compareToIgnoreCase(b));
Можна так:
names.sort(String::compareToIgnoreCase);
Приклад: обробка колекцій
Замість:
list.forEach(s -> System.out.println(s));
Ще коротше:
list.forEach(System.out::println);
Приклад: перетворення елементів
Замість:
List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> ints = numbers.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Можна:
List<Integer> ints = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
Детальніше про Stream API та утилітний клас Collectors ви дізнаєтеся на 30‑му рівні :P
4. Порівняння method reference і лямбда-виразів
Еквівалентність
Method reference і лямбда-вираз часто взаємозамінні. Обидва реалізують функціональний інтерфейс, якщо сигнатури збігаються.
Приклад:
Consumer<String> c1 = s -> System.out.println(s);
Consumer<String> c2 = System.out::println;
Коли краще використовувати method reference?
- Коли лямбда лише викликає наявний метод без додаткової логіки.
- Щоб підвищити читабельність, особливо в довгих ланцюжках викликів.
- Коли потрібно явно показати: «тут просто виклик методу».
Коли method reference не підходить?
- Якщо потрібна додаткова логіка (валідація, умови, обробка помилок).
- Якщо параметри потрібно перетворити перед викликом методу.
Приклад:
list.forEach(s -> {
if (s != null) System.out.println(s);
});
// Тут посилання на метод не підійде — лише лямбда.
5. Практика: переписуємо лямбда-вирази з використанням посилань на методи
Приклад 1: Виводимо імена тварин
List<String> animals = Arrays.asList("Кіт", "Собака", "Папуга");
animals.forEach(animal -> System.out.println(animal));
Стає:
animals.forEach(System.out::println);
Приклад 2: Перетворюємо рядки на числа
List<String> numbers = Arrays.asList("10", "20", "30");
List<Integer> ints = numbers.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Стає:
List<Integer> ints = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
Приклад 3: Сортування об’єктів за ім’ям
List<Animal> animalList = ...;
animalList.sort((a, b) -> a.getName().compareTo(b.getName()));
Стає:
animalList.sort(Comparator.comparing(Animal::getName));
Тут Animal::getName — посилання на нестатичний метод класу.
Приклад 4: Створення об’єктів через конструктор
Supplier<Dog> dogFactory = () -> new Dog();
Dog dog = dogFactory.get();
Стає:
Supplier<Dog> dogFactory = Dog::new;
Dog dog = dogFactory.get();
6. Як працює зіставлення сигнатур
Посилання на метод можна використовувати лише тоді, коли сигнатура цього методу збігається з абстрактним методом функціонального інтерфейсу.
@FunctionalInterface
interface IntToString {
String convert(int value);
}
public class Demo {
public static String intToHex(int value) {
return Integer.toHexString(value);
}
public static void main(String[] args) {
IntToString converter = Demo::intToHex;
System.out.println(converter.convert(255)); // ff
}
}
Тут Demo::intToHex підходить, тому що приймає int і повертає String.
7. Method reference і конструктори з параметрами
Якщо конструктор приймає параметри, method reference усе одно можна використовувати — за умови збігу сигнатур.
@FunctionalInterface
interface AnimalFactory {
Animal create(String name);
}
class Animal {
private String name;
public Animal(String name) { this.name = name; }
public String getName() { return name; }
}
AnimalFactory factory = Animal::new;
Animal cat = factory.create("Барсик");
System.out.println(cat.getName()); // Барсик
8. Типові помилки при використанні посилань на методи
Помилка № 1: Невідповідність сигнатур.
Якщо сигнатура методу не збігається з абстрактним методом інтерфейсу, компілятор видасть помилку. Наприклад, інтерфейс очікує два параметри, а посилання вказує на метод з одним параметром.
Помилка № 2: Спроба використати посилання на нестатичний метод класу без об’єкта.
Коли використовуєте форму Клас::метод, перший параметр інтерфейсу стає об’єктом виклику. Якщо переплутати кількість або порядок параметрів — отримаєте помилку зіставлення.
Помилка № 3: Використання method reference там, де потрібна додаткова логіка.
Якщо потрібна умова, логування або обробка винятків — використовуйте лямбду, а не посилання на метод.
Помилка № 4: Посилання на перевантажені методи.
Якщо в класі кілька методів з однаковою назвою, компілятор може не зрозуміти, який із них обрати. Іноді допомагає явне зазначення типу функціонального інтерфейсу.
Помилка № 5: Використання посилання на нестатичний метод без об’єкта.
Наприклад, String::toUpperCase коректно працює в map, тому що перший параметр — сам об’єкт String. Але поза відповідним контекстом, де очікується статичний метод, це призведе до помилки.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ