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. Конструктор

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

А пока давайте поговорим о 3 популярных способах передачи ссылки на метод:

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

Чтобы передать ссылку на метод объекта, нужно записать код вида объект::метод.
Этот код эквивалентен коду x -> объект.метод(x).

В качестве объекта могут фигурировать такие специальные переменные как this и super.

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

Чтобы передать ссылку на статический метод, нужно записать код вида класс::метод. Этот код будет преобразован к коду вида x -> класс.метод(x);

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

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

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