Какие знаешь классические функциональные интерфейсы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Классические функциональные интерфейсы в 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);
}
}
Таблица классических интерфейсов
| Интерфейс | Метод | Параметры | Возврат | Использование |
|---|---|---|---|---|
| Predicate | test | T | boolean | filter |
| Consumer | accept | T | void | forEach |
| Function | apply | T | R | map |
| Supplier | get | - | T | generate |
| UnaryOperator | apply | T | T | map для одного типа |
| BinaryOperator | apply | T, T | T | reduce |
| BiFunction | apply | T, U | R | Две разные переменные |
| BiConsumer | accept | T, U | void | forEach в Map |
| BiPredicate | test | T, U | boolean | filter с двумя параметрами |
Эти классические функциональные интерфейсы — основа функционального программирования в Java. Они позволяют писать более экспрессивный и ясный код с использованием lambda выражений и Stream API.