← Назад к вопросам

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

1.0 Junior🔥 191 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Лямбда и функциональные интерфейсы в Java

Лямбда-выражения и функциональные интерфейсы — революционная функциональность Java 8, которая привнесла функциональное программирование в язык. Это ключевой механизм современной Java разработки.

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

Функциональный интерфейс — это интерфейс ровно с одним абстрактным методом:

// ✅ Функциональный интерфейс
@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

// ✅ Также функциональный интерфейс (без аннотации)
public interface Printer {
    void print(String message);
}

// ❌ НЕ функциональный интерфейс (два метода)
public interface Invalid {
    void method1();
    void method2();
}

// ❌ НЕ функциональный интерфейс (0 методов)
public interface Empty {
}

@FunctionalInterface аннотация:

  • Опциональна, но рекомендуется
  • Указывает на то, что интерфейс функциональный
  • Компилятор проверит наличие ровно одного абстрактного метода

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

Лямбда-выражение — это анонимная реализация функционального интерфейса:

// Определение функционального интерфейса
@FunctionalInterface
public interface Operation {
    int execute(int a, int b);
}

// Способ 1: Анонимный класс (старый стиль)
Operation add = new Operation() {
    @Override
    public int execute(int a, int b) {
        return a + b;
    }
};

// Способ 2: Лямбда-выражение (новый стиль)
Operation add = (a, b) -> a + b;

// Использование
int result = add.execute(5, 3); // 8

Синтаксис лямбда-выражений

// Синтаксис: (параметры) -> тело

// 1. Без параметров
@FunctionalInterface
interface Greeting {
    void greet();
}
Greeting g = () -> System.out.println("Hello!");

// 2. Один параметр (скобки опциональны)
@FunctionalInterface
interface Multiplier {
    int multiply(int x);
}
Multiplier m = x -> x * 2;
Multiplier m2 = (x) -> x * 2; // Также правильно

// 3. Несколько параметров
@FunctionalInterface
interface Adder {
    int add(int a, int b);
}
Adder add = (a, b) -> a + b;

// 4. Несколько строк кода (требуются скобки)
Adder addVerbose = (a, b) -> {
    System.out.println("Adding " + a + " and " + b);
    return a + b;
};

// 5. Ссылка на метод
Adder addMethod = Integer::sum;

Встроенные функциональные интерфейсы (java.util.function)

Java предоставляет готовые функциональные интерфейсы:

// 1. Predicate<T> — проверка условия (T -> boolean)
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true

// 2. Consumer<T> — потребление значения (T -> void)
Consumer<String> print = System.out::println;
print.accept("Hello");

// 3. Function<T, R> — преобразование (T -> R)
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Java")); // 4

// 4. Supplier<T> — поставка значения (void -> T)
Supplier<String> supplier = () -> "Generated value";
System.out.println(supplier.get());

// 5. BiFunction<T, U, R> — функция с двумя параметрами
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
System.out.println(multiply.apply(3, 4)); // 12

// 6. BiConsumer<T, U> — потребление двух значений
BiConsumer<String, Integer> printNTimes = (s, n) -> {
    for (int i = 0; i < n; i++) System.out.println(s);
};

Связь между лямбда и интерфейсом

@FunctionalInterface
public interface StringTransformer {
    String transform(String input);
}

public class Main {
    public static void main(String[] args) {
        // Лямбда присваивается переменной типа интерфейса
        StringTransformer toUpperCase = s -> s.toUpperCase();
        StringTransformer toLowerCase = s -> s.toLowerCase();
        StringTransformer reverse = s -> new StringBuilder(s).reverse().toString();
        
        // Использование
        System.out.println(toUpperCase.transform("Hello")); // HELLO
        System.out.println(toLowerCase.transform("Hello")); // hello
        System.out.println(reverse.transform("Hello")); // olleH
    }
    
    // Лямбда как параметр функции
    static void apply(StringTransformer transformer, String input) {
        System.out.println(transformer.transform(input));
    }
}

// Вызов
Main.apply(s -> s.toUpperCase(), "hello"); // HELLO

Практические примеры

// Фильтрация списка
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());

// Преобразование списка
List<String> words = Arrays.asList("Java", "Spring", "Hibernate");
List<Integer> lengths = words.stream()
    .map(w -> w.length())
    .collect(Collectors.toList());

// Сортировка с лямбда
Collections.sort(words, (a, b) -> a.length() - b.length());

// Обработка элементов
numbers.forEach(n -> System.out.println(n * 2));

// Создание потока с лямбда
new Thread(() -> System.out.println("Running in thread")).start();

// Callback функция
interface DataFetcher {
    void onSuccess(String data);
}

void fetchData(DataFetcher callback) {
    callback.onSuccess("Data loaded");
}

fetchData(data -> System.out.println("Received: " + data));

Type Inference (Вывод типов)

Компилятор может выводить типы параметров из контекста:

// Компилятор знает, что a и b — int, потому что интерфейс Calculator имеет method(int a, int b)
Calculator calc = (a, b) -> a + b;

// Явное указание типов (опционально)
Calculator calc2 = (int a, int b) -> a + b;

// В Stream API тип выводится из коллекции
List<String> words = Arrays.asList("A", "BB", "CCC");
words.stream().forEach(w -> System.out.println(w)); // w тип String

Лямбда и исключения

@FunctionalInterface
interface Operation {
    int execute() throws IOException;
}

// Лямбда может выбросить исключение
Operation op = () -> {
    throw new IOException("Error");
};

// Обработка
try {
    op.execute();
} catch (IOException e) {
    e.printStackTrace();
}

Closure — захват переменных

int multiplier = 5; // Переменная должна быть effectively final

// Лямбда захватывает значение переменной
Function<Integer, Integer> multiply = x -> x * multiplier;
System.out.println(multiply.apply(3)); // 15

// ❌ ОШИБКА: нельзя изменять захваченную переменную
// multiplier = 10; // Compilation error!

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

Сокращенный синтаксис для лямбда:

// Лямбда
Function<String, Integer> length1 = s -> s.length();

// Ссылка на метод (эквивалентно)
Function<String, Integer> length2 = String::length;

// Другие примеры
Consumer<String> print = System.out::println;
Supplier<List<String>> listSupplier = ArrayList::new;
BiFunction<String, String, Boolean> equals = String::equals;

Таблица типов лямбда

ИнтерфейсПараметрыВозвращаетПример
Predicate<T>Tbooleann -> n > 0
Consumer<T>Tvoids -> print(s)
Function<T,R>TRs -> s.length()
Supplier<T>(none)T() -> "value"
BiFunction<T,U,R>T, UR(a,b) -> a+b
BiConsumer<T,U>T, Uvoid(a,b) -> print(a,b)
BiPredicate<T,U>T, Uboolean(a,b) -> a.equals(b)
UnaryOperator<T>TTx -> x * 2
BinaryOperator<T>T, TT(a,b) -> a+b