1. Функціональні методи

Якщо інтерфейс має тільки один метод, змінній цього типу-інтерфейсу можна присвоїти значення, задане лямбда-виразом (лямбда-функцією). Такі інтерфейси стали називати функціональними інтерфейсами (після додавання в Java підтримки лямбда-функцій).

Наприклад, у Java є інтерфейс Consumer<Тип> (Consumer == Споживач), який містить метод accept(Тип obj). Навіщо ж потрібен цей інтерфейс?

У Java 8 у колекцій з'явився метод forEach(), який дає змогу для кожного елемента колекції виконати якусь дію. І от саме для передавання дії в метод forEach() і використовується функціональний інтерфейс Consumer<T>.

Отак можна вивести всі елементи колекції на екран:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Привіт", "як", "справи?");

list.forEach( (s) -> System.out.println(s) );
Виведення всіх елементів колекції (з використанням лямбда-виразу)

Компілятор перетворить цей код на такий:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Привіт", "як", "справи?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s);
   }
});
Виведення всіх елементів колекції (з використанням анонімного класу)

Перший запис однозначно коротший за другий. І хоча читати код із лямбда-виразами непросто, читати код з анонімними внутрішніми класами часом іще складніше.



2. Посилання на метод

Натомість наш код із лямбда-виразом можна скоротити ще більше.

По-перше, можна випустити дужки навколо параметра s:

list.forEach( (s) -> System.out.println(s) );
Було
list.forEach( s -> System.out.println(s) );
Стало

Так можна робити, тільки якщо параметр один. Якщо параметрів декілька, потрібно використовувати дужки.

А по-друге, можна записати так:

list.forEach( System.out::println );
Найкомпактніший запис

Це все той самий запис. Зверніть увагу, що після println немає дужок.

Тут записано той самий код — виклик методу:

об'єкт::метод
x -> об'єкт.метод(x)

Подумайте самі: ми хотіли для кожного елемента колекції list виконувати якусь дію. Якщо ця дія — виклик однієї функції (як-от println()), було б розумно просто передати функцію в метод як параметр.

А як пояснити компілятору, що функцію потрібно саме передати, а не викликати? Для цього перед іменем методу ставимо не крапку, а дві двокрапки: одна двокрапка вже зайнята в тернарному операторі.

Це й є найпростішим і найкомпактнішим записом.



3. Конструктор

Посилання на методи за допомогою подвійної двокрапки дуже зручно використовувати, коли ми працюємо з потоками вводу-виведення. Ви переконаєтеся в цьому трохи згодом.

А наразі поговорімо про три популярні способи передачі посилання на метод:

Посилання на метод об'єкта

Для того щоб передати посилання на метод об'єкта, потрібно записати код такого формату: об'єкт::метод.
Цей код є еквівалентним коду x -> об'єкт.метод(x).

Об'єктом можуть бути такі спеціальні змінні, як this і super.

Посилання на метод класу

Для того щоб передати посилання на статичний метод, потрібно записати код такого формату: клас::метод. Цей код буде перетворено на код x -> клас.метод(x);

Посилання на конструктор

Конструктор за своєю поведінкою чимось схожий на статичний метод класу, тому на нього теж можна передати посилання. Відповідний код має такий вигляд: клас::new.

Наприклад, можна обійти стирання типів у колекцій і передати в метод toArray() посилання на конструктор, який створить потрібний масив: toArray( int[]::new );