Функциональные интерфейсы

Функциональный интерфейс — это интерфейс, у которого ровно один абстрактный метод. Именно такие интерфейсы можно реализовать через лямбда-выражение или ссылку на метод. В пакете java.util.function собраны готовые универсальные функциональные интерфейсы для типовых задач: преобразовать значение, проверить условие, что-то сделать с объектом, поставить значение.

Аннотация @FunctionalInterface не обязательна, но желательна: компилятор проверит, что у интерфейса действительно один абстрактный метод, и не даст случайно добавить второй.

Базовые интерфейсы

Function<T, R>       R apply(T t)        // преобразование T -> R
Predicate<T>         boolean test(T t)   // проверка
Consumer<T>          void accept(T t)    // действие без возврата
Supplier<T>          T get()             // поставщик значений
BiFunction<T, U, R>  R apply(T, U)       // два аргумента -> результат
UnaryOperator<T>     T apply(T t)        // T -> T
BinaryOperator<T>    T apply(T, T)       // (T, T) -> T

Примеры

Пример 1. Function

import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer, String> labelDistance = cm -> "Расстояние = " + cm + " см";
        System.out.println(labelDistance.apply(15));
        System.out.println(labelDistance.apply(120));
    }
}

Output:

Расстояние = 15 см
Расстояние = 120 см

Пример 2. Predicate

import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        Predicate<Integer> tooClose = d -> d < 10;
        Predicate<Integer> tooFar = d -> d > 100;
        Predicate<Integer> bad = tooClose.or(tooFar);

        System.out.println(bad.test(5));
        System.out.println(bad.test(50));
        System.out.println(bad.test(200));
    }
}

Output:

true
false
true

Пример 3. Consumer

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        Consumer<String> log = msg -> System.out.println("[LOG] " + msg);
        List<String> events = Arrays.asList("старт", "движение", "стоп");
        events.forEach(log);
    }
}

Output:

[LOG] старт
[LOG] движение
[LOG] стоп

Пример 4. Supplier

import java.util.function.Supplier;

public class SupplierDemo {
    public static void main(String[] args) {
        Supplier<Long> now = () -> System.currentTimeMillis() / 1000;
        System.out.println("UNIX-время (с): " + now.get());
        System.out.println("Повтор: " + now.get());
    }
}

Output:

UNIX-время (с): 1747300000
Повтор: 1747300000

(точные значения зависят от текущего времени).

Пример 5. Собственный @FunctionalInterface

public class CustomFunctional {
    @FunctionalInterface
    interface SensorReader {
        int read(String pin);
    }

    static int average(SensorReader reader, String pin, int samples) {
        int sum = 0;
        for (int i = 0; i < samples; i++) sum += reader.read(pin);
        return sum / samples;
    }

    public static void main(String[] args) {
        SensorReader fake = pin -> pin.length() * 100;
        System.out.println(average(fake, "A0", 4));
        System.out.println(average(fake, "ECHO", 4));
    }
}

Output:

200
400

Пример 6. Композиция Function

import java.util.function.Function;

public class FunctionCompose {
    public static void main(String[] args) {
        Function<Integer, Integer> plus10 = x -> x + 10;
        Function<Integer, Integer> times2 = x -> x * 2;

        Function<Integer, Integer> combo = plus10.andThen(times2);
        System.out.println(combo.apply(5));  // (5+10)*2

        Function<Integer, Integer> combo2 = plus10.compose(times2);
        System.out.println(combo2.apply(5)); // (5*2)+10
    }
}

Output:

30
20

Подводные камни

Предупреждение

  • @FunctionalInterface запрещает второй абстрактный метод, но допускает default- и static-методы.

  • Примитивные типы при использовании Function<Integer, Integer> боксятся в объекты. Для производительности есть IntFunction, IntPredicate, IntUnaryOperator и т.п.

  • Predicate.and / or / negate возвращают новый Predicate — исходный не меняется.

  • Не путайте andThen и compose: a.andThen(b) сначала a, потом b; a.compose(b) сначала b, потом a.

См. также

Примечание

Материал подготовлен по мотивам Oracle Java Tutorials и документации пакета java.util.function, распространяется в рамках Oracle Free Documentation License. Текст написан своими словами, примеры кода — оригинальные.