Функциональные интерфейсы, лямбды, указатели на методы

Здесь в качестве примера используется интерфейс OnDialListener:

package online.javanese.basics.oop.functionalInterface;

public abstract class Phone {
    
    // всякое разное
    
    public interface OnDialListener {
        void onDial(String number);
    }
}

В Java 7 можно передать код другому объекту, создав анонимный класс, который реализует интерфейс:

        phone.setOnDialListener(new OnDialListener() {
            @Override
            public void onDial(String number) {
                System.out.println("Этот код запущен объектом " + phone + ".");
            }
        });

Выглядит это довольно громоздко, ведь нужно было объявить всего один метод. В Java 8 появилось понятие функционального интерфейса, которое позволяет рассматривать функцию (метод) как реализацию интерфейса.

Лямбды

В Java 8 можно использовать лямбды. Lambda function или arrow function — это функция, объявленная не как метод определённого класса. Это выглядит так:

        phone.setOnDialListener(
                (number) -> {
                    System.out.println("Этот код запущен объектом " + phone + ".");
                });

В круглых скобках перечисляются аргументы, в фигурных — тело метода (действия). Если аргумент один, круглые скобки можно опустить. Если выражение одно, фигурные скобки можно опустить, а точка с запятой не ставится.

        phone.setOnDialListener(
                number -> System.out.println("Этот код запущен объектом " + phone + "."));

Указатели на методы

Java 8 может рассматривать метод класса как реализацию функционального интерфейса:

package online.javanese.basics.oop.functionalInterface;

public class MethodReferenceExample {
    
    public static void main(String[] args) {
        Phone phone = new Nexus5();
        phone.setOnDialListener(MethodReferenceExample::onDial);
        // эквивалент
        // phone.setOnDialListener(number -> MethodReferenceExample.onDial(number));
    }
    
    private static void onDial(String number) {
        System.out.println("Я всё знаю! Набирается номер: " + number);
    }
    
}

Можно также указать на метод конкретного объекта:

    public static void main(String[] args) {
        
        class Spy {
            void reportPhoneCall(String number) {
                System.out.println("Штаб! Кто-то звонит на номер " + number);
            }
        }
        
        Phone phone = new Nexus5();
        Spy spy = new Spy();
        phone.setOnDialListener(spy::reportPhoneCall);
        // эквивалент
        // phone.setOnDialListener(number -> spy.reportPhoneCall(number));
    }

Указатель на конструктор

package online.javanese.basics.oop.functionalInterface;

public class ConstructorReferenceExample {

    public static void main(String[] args) {
        PhoneFactory factory = Nexus5::new;
        // эквивалент
        // PhoneFactory factory = () -> new Nexus5();
        System.out.println(factory.createNew());
        // Nexus5(serialNumber=NEX-119e7998-3789-4361-b931-e04af3b68854, 
        // IMEI=7324738727401515452, batteryVoltage=3900mV)
    }

    @FunctionalInterface
    private interface PhoneFactory {
        Phone createNew();
    }

}

Лямбда или указатель — что выбрать?

Как удобно. Это ни на что не влияет.

Примеры стандартных функциональных интерфейсов

Runnable

Любое действие. Не принимает аргемунтов, не возвращает значения.

@FunctionalInterface
public interface Runnable {
    public void run();
}

IntConsumer

Потребитель целого числа. Принимает int, ничего не возвращает.

@FunctionalInterface
public interface IntConsumer {
    void accept(int value);
}

IntSupplier

Поставщик целого числа. Ничего не принимает, возвращает int.

@FunctionalInterface
public interface IntSupplier {
    int getAsInt();
}

IntToDoubleFunction

Функция, которая принимает int и возвращает double.

@FunctionalInterface
public interface IntToDoubleFunction {
    double applyAsDouble(int value);
}

Таких интерфейсов довольно много и некоторые из них мы ещё рассмотрим.

Комментарии к уроку

Сообщить об ошибке