Тема 11. Functional Interfaces and Lambdas
Функциональные интерфейсы и лямбды в Java нужны для передачи поведения как данных. Это не замена ООП, а дополнительный уровень выразительности: доменную модель вы по-прежнему описываете классами, а локальные правила и стратегии передаете как функции.
Базовая идея
До Java 8 часто приходилось писать анонимные классы даже для простого поведения. Лямбды сократили этот шаблонный код и сделали намерение более очевидным.
Функциональный интерфейс это интерфейс с одним абстрактным методом (SAM - Single Abstract Method).
@FunctionalInterface
interface MathOp {
int apply(int a, int b);
}
MathOp add = (a, b) -> a + b;
System.out.println(add.apply(2, 3)); // 5
Где используется функциональный стиль в Java
- Коллекции (
sort,removeIf,forEach). - Stream API (
map,filter,reduce). - Асинхронность (
CompletableFuture). - События, коллбеки, стратегии.
- Пулы потоков (
Runnable,Callable).
Стандартные functional interfaces
Predicate<T>:T -> boolean.Function<T, R>:T -> R.Consumer<T>:T -> void.Supplier<T>:() -> T.UnaryOperator<T>:T -> T.BinaryOperator<T>:(T, T) -> T.BiFunction<T, U, R>:(T, U) -> R.
Пример 1. Predicate + Function
Predicate<String> longName = s -> s.length() >= 4;
Function<String, String> upper = String::toUpperCase;
String result = List.of("ann", "alex").stream()
.filter(longName)
.map(upper)
.findFirst()
.orElse("NONE");
System.out.println(result); // ALEX
Пример 2. Consumer + Supplier
Supplier<UUID> idSupplier = UUID::randomUUID;
Consumer<UUID> printer = id -> System.out.println("ID=" + id);
UUID id = idSupplier.get();
printer.accept(id);
Синтаксис лямбд
Варианты:
() -> 42
x -> x * 2
(a, b) -> a + b
(String s) -> s.trim()
(a, b) -> {
int r = a + b;
return r;
}
Правило: чем короче и проще лямбда, тем лучше читаемость.
Method references
Method reference это сокращенный синтаксис, когда лямбда только вызывает существующий метод.
Виды:
ClassName::staticMethodobj::instanceMethodClassName::instanceMethodClassName::new(ссылка на конструктор)
Пример:
List<String> names = new ArrayList<>(List.of("Bob", "Ann", "alex"));
names.sort(String::compareToIgnoreCase);
System.out.println(names); // [alex, Ann, Bob]
Замыкания и effectively final
Лямбды могут читать локальные переменные внешнего метода, если они final или effectively final.
int min = 3;
Predicate<String> p = s -> s.length() >= min;
Нельзя:
int min = 3;
min++; // после этого переменная уже не effectively final
Это ограничение защищает от неочевидных проблем с конкурентностью и временем жизни переменных.
Функциональные композиции
Вы можете собирать функции как конвейер:
Function<String, String> trim = String::trim;
Function<String, String> upper = String::toUpperCase;
Function<String, String> pipeline = trim.andThen(upper);
System.out.println(pipeline.apply(" java ")); // JAVA
Для условий:
Predicate<String> notBlank = s -> !s.isBlank();
Predicate<String> shortWord = s -> s.length() <= 5;
Predicate<String> rule = notBlank.and(shortWord);
System.out.println(rule.test("java")); // true
Практические паттерны
Strategy через лямбду
interface DiscountStrategy {
double apply(double amount);
}
double checkout(double amount, DiscountStrategy strategy) {
return strategy.apply(amount);
}
double result = checkout(1000, a -> a * 0.9);
Callback для повторно используемой инфраструктуры
void withLogging(String opName, Runnable action) {
long start = System.nanoTime();
try {
action.run();
} finally {
System.out.println(opName + " took " + (System.nanoTime() - start));
}
}
Частые ошибки
- Слишком длинные лямбды с бизнес-логикой на пол-экрана.
- Побочные эффекты внутри stream-операций.
- Непонятные имена аргументов (
x,y,z) в сложном коде. - Избыточные собственные functional interfaces, когда есть готовые из
java.util.function.
Когда лучше обычный метод
Лямбда не обязательна всегда. Вынесите логику в именованный метод, если:
- Нужна повторная переиспользуемость в нескольких местах.
- Лямбда становится длинной и плохо читаемой.
- Требуется отдельное тестирование сложной логики.
Что важно запомнить
- Функциональные интерфейсы это контракт «поведение как значение».
- Лямбды сокращают шаблонный код и улучшают выразительность.
- Лучший стиль: короткие лямбды, чистые функции, минимум побочных эффектов.