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

Какие знаешь классические функциональные интерфейсы?

2.0 Middle🔥 201 комментариев
#Stream API и функциональное программирование#Основы Java

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

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

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

Классические функциональные интерфейсы в Java

Функциональные интерфейсы (Functional Interfaces) — это интерфейсы с одним абстрактным методом. Введены в Java 8 и являются основой для lambda выражений и Stream API. В java.util.function пакете находятся основные классические интерфейсы.

1. Predicate<T> — Проверка условия

Возвращает boolean на основе входного значения:

public interface Predicate<T> {
  boolean test(T t);
  
  default Predicate<T> and(Predicate<? super T> other) { ... }
  default Predicate<T> or(Predicate<? super T> other) { ... }
  default Predicate<T> negate() { ... }
}

// Примеры использования
Predicate<Integer> isEven = num -> num % 2 == 0;
boolean result = isEven.test(4);  // true

// В Stream API
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
  .filter(isEven)  // Predicate используется в filter
  .collect(Collectors.toList());

// Комбинирование предикатов
Predicate<String> isLonger = str -> str.length() > 3;
Predicate<String> combined = isEven.and(isLonger);

2. Consumer<T> — Выполнение операции

Принимает значение и выполняет операцию (ничего не возвращает):

public interface Consumer<T> {
  void accept(T t);
  
  default Consumer<T> andThen(Consumer<? super T> after) { ... }
}

// Примеры использования
Consumer<String> printer = message -> System.out.println(message);
printer.accept("Hello");  // Hello

// В Stream API для side effects
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
numbers.forEach(num -> System.out.println(num));
// или
numbers.forEach(System.out::println);

// Чейнирование Consumer'ов
Consumer<String> consumer1 = str -> System.out.println("First: " + str);
Consumer<String> consumer2 = str -> System.out.println("Second: " + str);
Consumer<String> combined = consumer1.andThen(consumer2);
combined.accept("Hello");  // First: Hello, Second: Hello

3. Function<T, R> — Преобразование значения

Принимает значение типа T и возвращает значение типа R:

public interface Function<T, R> {
  R apply(T t);
  
  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { ... }
  default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { ... }
}

// Примеры использования
Function<Integer, String> intToString = num -> "Number: " + num;
String result = intToString.apply(5);  // Number: 5

// В Stream API для map
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<String> stringNumbers = numbers.stream()
  .map(intToString)  // Преобразование каждого элемента
  .collect(Collectors.toList());

// Композиция функций
Function<Integer, Integer> multiply = x -> x * 2;
Function<Integer, String> toString = x -> "Result: " + x;
Function<Integer, String> combined = multiply.andThen(toString);
String output = combined.apply(5);  // Result: 10

4. Supplier<T> — Поставка значения

Не принимает аргументов, только возвращает значение:

public interface Supplier<T> {
  T get();
}

// Примеры использования
Supplier<Integer> randomNumber = () -> new Random().nextInt(100);
int num = randomNumber.get();

// Lazy инициализация
Supplier<Database> dbSupplier = () -> new Database("localhost:5432");
Database db = dbSupplier.get();  // Database создаётся только при вызове get()

// В Stream API для создания значений
List<Double> randomDoubles = Stream
  .generate(() -> Math.random())
  .limit(5)
  .collect(Collectors.toList());

5. UnaryOperator<T> — Операция с одним аргументом

Принимает и возвращает значение одного типа (extends Function<T, T>):

public interface UnaryOperator<T> extends Function<T, T> {
  // наследует apply(T t) -> T
  
  static <T> UnaryOperator<T> identity() { ... }
}

// Примеры использования
UnaryOperator<Integer> square = x -> x * x;
int result = square.apply(5);  // 25

// В Stream API
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> squared = numbers.stream()
  .map(square)
  .collect(Collectors.toList());

// Чейнирование унарных операторов
UnaryOperator<String> toUpper = String::toUpperCase;
UnaryOperator<String> reverse = str -> new StringBuilder(str).reverse().toString();
UnaryOperator<String> combined = toUpper.andThen(reverse);
String result = combined.apply("hello");  // OLLEH

6. BinaryOperator<T> — Операция с двумя аргументами

Принимает два значения одного типа и возвращает один результат (extends BiFunction<T, T, T>):

public interface BinaryOperator<T> extends BiFunction<T, T, T> {
  // наследует apply(T t1, T t2) -> T
  
  static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { ... }
  static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { ... }
}

// Примеры использования
BinaryOperator<Integer> add = (a, b) -> a + b;
int sum = add.apply(5, 3);  // 8

// В Stream API для reduce
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int totalSum = numbers.stream()
  .reduce(0, add);  // 15

// Максимум элементов
BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.naturalOrder());
int maximum = numbers.stream()
  .reduce(Integer.MIN_VALUE, max);

// Строковой конкатенатор
BinaryOperator<String> concat = (a, b) -> a + ", " + b;
String result = Arrays.asList("Alice", "Bob", "Charlie").stream()
  .reduce("", concat);

7. BiFunction<T, U, R> — Функция с двумя аргументами

Принимает два аргумента разных типов и возвращает результат:

public interface BiFunction<T, U, R> {
  R apply(T t, U u);
  
  default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) { ... }
}

// Примеры использования
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
int result = multiply.apply(5, 3);  // 15

// Для комбинирования данных
BiFunction<String, Integer, String> repeat = (str, count) -> {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < count; i++) {
    sb.append(str);
  }
  return sb.toString();
};
String output = repeat.apply("Ha", 3);  // HaHaHa

// С andThen
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
BiFunction<Integer, Integer, String> sumToString = 
  sum.andThen(result -> "Sum: " + result);
String formatted = sumToString.apply(5, 3);  // Sum: 8

8. BiConsumer<T, U> — Операция с двумя аргументами

Принимает два аргумента и выполняет операцию:

public interface BiConsumer<T, U> {
  void accept(T t, U u);
  
  default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) { ... }
}

// Примеры использования
BiConsumer<String, Integer> printer = (name, age) -> 
  System.out.println(name + " is " + age + " years old");
printer.accept("Alice", 30);

// В Map.forEach
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
ages.forEach((name, age) -> System.out.println(name + ": " + age));

9. BiPredicate<T, U> — Проверка условия с двумя аргументами

public interface BiPredicate<T, U> {
  boolean test(T t, U u);
}

// Примеры использования
BiPredicate<Integer, Integer> isGreater = (a, b) -> a > b;
boolean result = isGreater.test(5, 3);  // true

Практический пример комбинирования интерфейсов

public class FunctionalInterfacesExample {
  
  public static void main(String[] args) {
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    
    // Predicate — фильтрация по длине
    Predicate<String> longerThan3 = str -> str.length() > 3;
    
    // Function — преобразование в верхний регистр
    Function<String, String> toUpperCase = String::toUpperCase;
    
    // Consumer — вывод результата
    Consumer<String> print = System.out::println;
    
    // Комбинирование
    names.stream()
      .filter(longerThan3)
      .map(toUpperCase)
      .forEach(print);
    // ALICE, CHARLIE, DAVID
    
    // BinaryOperator — объединение результатов
    BinaryOperator<Integer> sum = Integer::sum;
    int total = Arrays.asList(1, 2, 3, 4, 5).stream()
      .reduce(0, sum);
    System.out.println("Total: " + total);
  }
}

Таблица классических интерфейсов

ИнтерфейсМетодПараметрыВозвратИспользование
PredicatetestTbooleanfilter
ConsumeracceptTvoidforEach
FunctionapplyTRmap
Supplierget-Tgenerate
UnaryOperatorapplyTTmap для одного типа
BinaryOperatorapplyT, TTreduce
BiFunctionapplyT, URДве разные переменные
BiConsumeracceptT, UvoidforEach в Map
BiPredicatetestT, Ubooleanfilter с двумя параметрами

Эти классические функциональные интерфейсы — основа функционального программирования в Java. Они позволяют писать более экспрессивный и ясный код с использованием lambda выражений и Stream API.

Какие знаешь классические функциональные интерфейсы? | PrepBro