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

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

1.2 Junior🔥 71 комментариев
#ООП

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

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

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

Функциональные интерфейсы в Java: практические примеры и применение

Функциональные интерфейсы (Functional Interfaces) — это один из главных нововведений Java 8, которые открыли дверь к функциональному программированию. Давайте разберемся, что это такое и как их использовать.

Определение функционального интерфейса

Функциональный интерфейс — это интерфейс с ровно одним abstract методом. Он может иметь много default методов, но abstract метод только один.

// Функциональный интерфейс (1 abstract метод)
@FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);  // Единственный abstract метод
    
    // default методы можно добавлять
    default void printResult(int result) {
        System.out.println("Result: " + result);
    }
}

// Не функциональный интерфейс (2 abstract метода)
public interface NotFunctional {
    void method1();
    void method2();
    // ОШИБКА: много abstract методов
}

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

Java 8 предоставил готовые функциональные интерфейсы в пакете java.util.function:

1. Predicate<T> — проверка условия (true/false)

// Сигнатура: boolean test(T t)
public interface Predicate<T> {
    boolean test(T t);
}

// Пример 1: фильтрация по числам
Predicate<Integer> isEven = n -> n % 2 == 0;
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
    .filter(isEven)
    .toList();
// Результат: [2, 4, 6]

// Пример 2: фильтрация пользователей
Predicate<User> isActive = user -> user.isActive();
Predicate<User> isPremium = user -> user.isPremium();
Predicate<User> isActiveAndPremium = isActive.and(isPremium);

List<User> premiumUsers = users.stream()
    .filter(isActiveAndPremium)
    .toList();

// Пример 3: валидация
Predicate<String> isLongEnough = str -> str.length() >= 8;
Predicate<String> hasDigit = str -> str.matches(".*\\d.*");
Predicate<String> isValidPassword = isLongEnough.and(hasDigit);

if (isValidPassword.test("password123")) {
    System.out.println("Valid password");
}

2. Function<T, R> — преобразование (T -> R)

// Сигнатура: R apply(T t)
public interface Function<T, R> {
    R apply(T t);
}

// Пример 1: преобразование строк
Function<String, Integer> stringLength = str -> str.length();
Integer length = stringLength.apply("Hello");  // 5

// Пример 2: преобразование объектов
Function<User, UserDTO> userToDTO = user -> new UserDTO(
    user.getId(),
    user.getName(),
    user.getEmail()
);

List<UserDTO> dtos = users.stream()
    .map(userToDTO)
    .toList();

// Пример 3: цепочка преобразований
Function<String, String> trim = str -> str.trim();
Function<String, String> toUpperCase = str -> str.toUpperCase();
Function<String, Integer> parseInteger = Integer::parseInt;

Function<String, Integer> composed = trim.andThen(toUpperCase).andThen(parseInteger);
Integer result = composed.apply(" 42 ");  // 42

// Пример 4: в коллекциях
Map<String, Integer> ages = new HashMap<>();
ages.put("John", 25);
ages.put("Jane", 30);

Function<Integer, String> ageToCategory = age -> age < 30 ? "young" : "adult";
Map<String, String> categories = ages.entrySet().stream()
    .collect(toMap(
        Map.Entry::getKey,
        e -> ageToCategory.apply(e.getValue())
    ));
// {John=young, Jane=adult}

3. Consumer<T> — потребление (выполнение действия)

// Сигнатура: void accept(T t)
public interface Consumer<T> {
    void accept(T t);
}

// Пример 1: печать элементов
Consumer<String> printer = str -> System.out.println(str);
List<String> fruits = List.of("apple", "banana", "orange");
fruits.forEach(printer);

// Пример 2: логирование
Consumer<User> logger = user -> log.info("User processed: {}", user.getName());
users.forEach(logger);

// Пример 3: отправка уведомлений
Consumer<Order> notifier = order -> emailService.sendConfirmation(order);
orders.stream()
    .filter(order -> order.isPaid())
    .forEach(notifier);

// Пример 4: цепочка действий
Consumer<String> uppercase = str -> System.out.println(str.toUpperCase());
Consumer<String> lowercase = str -> System.out.println(str.toLowerCase());
Consumer<String> chained = uppercase.andThen(lowercase);

chained.accept("Hello");  // HELLO, hello

4. Supplier<T> — поставщик (создание)

// Сигнатура: T get()
public interface Supplier<T> {
    T get();
}

// Пример 1: ленивое вычисление
Supplier<LocalDateTime> now = LocalDateTime::now;
LocalDateTime time1 = now.get();
LocalDateTime time2 = now.get();
// каждый раз возвращает текущее время

// Пример 2: создание объектов
Supplier<StringBuilder> stringBuilderSupplier = StringBuilder::new;
StringBuilder sb1 = stringBuilderSupplier.get();
StringBuilder sb2 = stringBuilderSupplier.get();
// Разные объекты!

// Пример 3: фабрика с параметром
Supplier<User> anonymousUserSupplier = () -> new User("Anonymous", null);
User guest = anonymousUserSupplier.get();

// Пример 4: конфигурация
Supplier<DatabaseConnection> connectionSupplier = () -> {
    Properties props = new Properties();
    props.load(new FileInputStream("db.properties"));
    return new DatabaseConnection(props);
};

DatabaseConnection conn = connectionSupplier.get();

// Пример 5: Optional.orElseGet
Optional<User> user = findUser("john");
User result = user.orElseGet(() -> new User("default", null));

5. UnaryOperator<T> — унарная операция (T -> T)

// Расширяет Function<T, T>
public interface UnaryOperator<T> extends Function<T, T> {}

// Пример 1: преобразование в себе
UnaryOperator<Integer> square = n -> n * n;
Integer result = square.apply(5);  // 25

// Пример 2: преобразование списка
UnaryOperator<String> trim = String::trim;
List<String> words = List.of(" hello ", " world ");
words = words.stream()
    .map(trim)
    .toList();

// Пример 3: для чисел
UnaryOperator<Double> negate = n -> -n;
Double price = 100.0;
Double discount = negate.apply(price);  // -100.0

6. BinaryOperator<T> — бинарная операция (T, T -> T)

// Расширяет BiFunction<T, T, T>
public interface BinaryOperator<T> extends BiFunction<T, T, T> {}

// Пример 1: сумма
BinaryOperator<Integer> sum = Integer::sum;  // или (a, b) -> a + b
Integer result = sum.apply(3, 4);  // 7

// Пример 2: максимум
BinaryOperator<Integer> max = Integer::max;
Integer maxValue = max.apply(10, 20);  // 20

// Пример 3: для строк
BinaryOperator<String> concat = String::concat;
String result = concat.apply("Hello", " World");  // Hello World

// Пример 4: в reduce
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
Integer sum = numbers.stream()
    .reduce(0, BinaryOperator.identity());  // 1+2+3+4+5 = 15

// Пример 5: кастомная операция
BinaryOperator<User> merge = (u1, u2) -> {
    u1.addFriends(u2.getFriends());
    return u1;
};

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

// Кастомный функциональный интерфейс для комплексной логики
@FunctionalInterface
public interface OrderProcessor {
    void process(Order order, PaymentGateway gateway, NotificationService notifier);
}

// Использование
OrderProcessor processor = (order, gateway, notifier) -> {
    if (gateway.charge(order.getAmount())) {
        order.markAsPaid();
        notifier.sendConfirmation(order);
    } else {
        notifier.sendFailure(order);
    }
};

// Применение
processor.process(myOrder, paymentGateway, emailService);

Практический пример: Stream API

@Service
public class UserService {
    private final UserRepository userRepository;
    
    public List<UserDTO> getActiveUsersSorted() {
        return userRepository.findAll().stream()
            // filter использует Predicate
            .filter(user -> user.isActive())
            .filter(user -> user.getCreatedAt().isAfter(LocalDateTime.now().minusMonths(1)))
            
            // map использует Function
            .map(user -> new UserDTO(
                user.getId(),
                user.getName().toUpperCase(),
                user.getEmail()
            ))
            
            // sorted использует Comparator (BinaryOperator)
            .sorted(Comparator.comparing(UserDTO::getName))
            
            // collect использует Supplier, Consumer
            .toList();
    }
    
    public void notifyUsers() {
        userRepository.findAll().stream()
            .filter(user -> user.hasNewMessages())
            // forEach использует Consumer
            .forEach(user -> emailService.send(
                user.getEmail(),
                "You have new messages"
            ));
    }
}

Method Reference (сокращенная запись)

// Вместо lambda можно использовать method reference

// Вместо этого:
List<String> words = List.of("hello", "world");
words.forEach(str -> System.out.println(str));

// Можно писать так:
words.forEach(System.out::println);

// Вместо этого:
List<Integer> numbers = List.of(1, 2, 3);
UnaryOperator<Integer> square = n -> n * n;

// Используй:
Function<Integer, Integer> square = n -> n * n;
// Или используй constructor reference:
Supplier<User> userSupplier = User::new;

// Типы method reference:
User::getName              // instance method
User::new                  // constructor
Integer::parseInt          // static method
List::add                  // method of arbitrary object

Вывод

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

  1. Predicate<T> — проверка условия (boolean test)
  2. Function<T, R> — преобразование (apply)
  3. Consumer<T> — действие (accept)
  4. Supplier<T> — создание (get)
  5. UnaryOperator<T> — унарная операция (T -> T)
  6. BinaryOperator<T> — бинарная операция (T, T -> T)

Они делают код:

  • Более функциональным — легче работать с потоками данных
  • Более читаемым — lambda выражения лучше, чем anonymous классы
  • Более переиспользуемым — одна функция используется во многих местах
  • Более декларативным — описываем ЧТО делать, не КАК

В современной Java функциональные интерфейсы — это стандартный способ работы с callbacks, асинхронностью и трансформациями данных.

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