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

Ако даден интерфейс има само един метод , на променлива от този тип интерфейс може да бъде присвоена стойност, дадена от ламбда израз (ламбда функция). Такива интерфейси станаха известни като функционални интерфейси (след като Java добави поддръжка за ламбда функции).

Например Java има Consumer<Type>интерфейс, който има accept(Type obj)метод. Защо е необходим този интерфейс?

В Java 8 колекциите имат forEach()метод, който ви позволява да извършвате няHowво действие за всеки елемент от колекцията . И тук Consumer<T>функционалният интерфейс се използва за предаване на действието на forEach()метода.

Ето How можете да покажете всички елементи на колекция :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
Показване на всички елементи на колекция (с помощта на ламбда израз)

Компилаторът ще преобразува codeа по-горе в codeа по-долу:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
Показване на всички елементи на колекция (с помощта на анонимен клас)

Първата version определено е по-кратка от втората. И докато codeът с ламбда изрази е труден за четене, codeът с анонимни вътрешни класове понякога е дори по-труден за четене.



2. Референтен метод

Нашият code на ламбда израз обаче може да бъде написан дори по-кратко.

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

list.forEach( (s) -> System.out.println(s) );
Преди
list.forEach( s -> System.out.println(s) );
След

Това може да стане само ако има един параметър . Ако има няколко параметъра, тогава трябва да използвате скоби .

И второ, можете да го напишете така:

list.forEach( System.out::println );
Най-компактна нотация

Това е абсолютно същата нотация. Обърнете внимание, че няма скоби след println.

Тук имаме същия code — извикване на метод:

object::method
x -> object.method(x)

Помислете за това: искахме да извършим няHowво действие за всеки елемент от listколекцията. Ако действието е едно извикване на функция (като println()), тогава има смисъл просто да прехвърлите функцията на метода като параметър.

Но How да обясним на компилатора, че искаме да предадем метода, instead of да го извикаме? За да направим това, instead of оператора точка, използваме две двоеточия преди името на метода. Едно двоеточие вече се използва за указване на троичния оператор.

Това е най-простият и компактен запис.



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

Препратките към методи с двойно двоеточие са много удобни, когато работим с I/O потоци. Ще видите това малко по-късно.

Междувременно, нека поговорим за 3 популярни начина за предаване на препратка към метод:

Препратка към метод на обект

За да предадете препратка към метод на обект, трябва да напишете нещо като . Този code е еквивалентен на .object::method
x -> object.method(x)

Специалните thisи superпроменливите могат да се използват като обект.

Препратка към метод на клас

За да предадете препратка към статичен метод, трябва да напишете нещо като . Този code се преобразува в code катоclass::methodx -> class.method(x);

Препратка към конструктор

Конструкторът се държи подобно на метод на статичен клас, така че можете също да предадете препратка към конструктор. Ето How изглежда :.class::new

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