Это просто, как чайник – налил воды, нажал кнопку, получил кипяток.
В Java функциональные интерфейсы являются интерфейсами, которые содержат только один абстрактный метод (но могут иметь несколько статических и/или default методов). Они часто используются в лямбда-выражениях и потоках (streams). В стандартной библиотеке Java уже есть несколько таких интерфейсов:
Основные функциональные интерфейсы в Java:
java.lang.Runnable
- Абстрактный метод:
void run()
- Используется для описания задачи, которая должна быть выполнена в отдельном потоке.
java.util.concurrent.Callable<T>
- Абстрактный метод:
T call() throws Exception
- Похож на
Runnable
, но возвращает результат и может выбрасывать исключение.
Интерфейсы из пакета java.util.function
:
Пакет java.util.function
содержит множество функциональных интерфейсов, которые полезны для работы с лямбда-выражениями и потоками:
Predicate<T>
- Абстрактный метод:
boolean test(T t)
- Используется для проверки условия (например, фильтрации).
Function<T, R>
- Абстрактный метод:
R apply(T t)
- Преобразует объект типа
T
в объект типаR
.
Consumer<T>
- Абстрактный метод:
void accept(T t)
- Принимает объект типа
T
, но ничего не возвращает (используется для операций, не возвращающих результат, например, печать).
Supplier<T>
- Абстрактный метод:
T get()
- Не принимает аргументов, но возвращает результат типа
T
(используется для ленивой генерации данных).
UnaryOperator<T>
- Наследуется от
Function<T, T>
. - Абстрактный метод:
T apply(T t)
- Преобразует объект типа
T
в другой объект того же типа.
BinaryOperator<T>
- Наследуется от
BiFunction<T, T, T>
. - Абстрактный метод:
T apply(T t1, T t2)
- Принимает два аргумента одного типа и возвращает результат того же типа.
BiFunction<T, U, R>
- Абстрактный метод:
R apply(T t, U u)
- Преобразует объекты типов
T
иU
в объект типаR
.
BiConsumer<T, U>
- Абстрактный метод:
void accept(T t, U u)
- Принимает два аргумента и не возвращает результата.
- Абстрактный метод:
BiPredicate<T, U>
- Абстрактный метод:
boolean test(T t, U u)
- Используется для проверки условия с двумя аргументами.
- Абстрактный метод:
Примеры специализированных интерфейсов:
Java также предоставляет специализированные версии функциональных интерфейсов для примитивов int
, long
, и double
, чтобы избежать автоупаковки/распаковки:
IntPredicate
,LongPredicate
,DoublePredicate
: работают с примитивами.IntFunction<R>
,LongFunction<R>
,DoubleFunction<R>
: преобразуют примитивы в другие типы.IntConsumer
,LongConsumer
,DoubleConsumer
: потребляют примитивы.IntSupplier
,LongSupplier
,DoubleSupplier
: поставляют примитивы.
Эти функциональные интерфейсы широко используются в Java для более лаконичного и функционального программирования.
А что по поводу Comparable?
Comparable
тоже можно рассматривать как функциональный интерфейс в Java, поскольку он содержит один абстрактный метод, но с оговорками:
Comparable<T>
- Абстрактный метод:
int compareTo(T o)
- Описание:
- Используется для сравнения текущего объекта с другим объектом того же типа. Возвращает отрицательное число, ноль или положительное число, если текущий объект меньше, равен или больше переданного объекта соответственно.
- Пример использования:
String
,Integer
и многие другие классы реализуют интерфейсComparable
для естественного упорядочивания.
Можно ли считать Comparable
функциональным интерфейсом?
С технической точки зрения, Comparable<T>
соответствует требованиям функционального интерфейса, так как имеет один абстрактный метод (compareTo
). Это позволяет использовать его в лямбда-выражениях. Однако в реальности Comparable
редко применяется в таком контексте, поскольку его основное назначение — определение естественного порядка объектов.
Тем не менее, его можно использовать в функциональном стиле, например, в методах, где ожидается Comparator
(который является функциональным интерфейсом и более часто применяется в лямбдах):
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.sort((s1, s2) -> s1.compareTo(s2));
В этом примере метод compareTo
из интерфейса Comparable
фактически используется в лямбда-выражении для сортировки списка.
A Comparator?
Comparator
— это более гибкий функциональный интерфейс в Java, который используется для сравнения двух объектов. Он часто применяется, когда нужно определить порядок элементов в коллекциях или сортировать их по нестандартным критериям.
Comparator<T>
- Абстрактный метод:
int compare(T o1, T o2)
- Описание:
- Сравнивает два объекта
o1
иo2
. Возвращает отрицательное число, ноль или положительное число, еслиo1
меньше, равен или большеo2
соответственно.
Преимущества Comparator
над Comparable
:
- Гибкость:
Comparator
позволяет определять несколько критериев сортировки для одного и того же класса. В то время какComparable
определяет “естественный” порядок (одно определение на класс),Comparator
позволяет создавать различные критерии сортировки.- Функциональный интерфейс:
Comparator
является функциональным интерфейсом, поэтому его можно использовать в лямбда-выражениях и передавать в методы, которые принимают интерфейсы функционального типа.
Методы по умолчанию и статические методы:
Comparator
также содержит полезные статические и default методы для создания компараторов:
comparing
:- Используется для создания
Comparator
на основе функции, извлекающей ключ. - Пример:
List<String> list = Arrays.asList("apple", "banana", "cherry"); list.sort(Comparator.comparing(String::length)); // Сортировка по длине строки
reversed
:- Инвертирует порядок компаратора.
- Пример:
list.sort(Comparator.comparing(String::length).reversed()); // Сортировка по длине строки в обратном порядке
thenComparing
:- Используется для создания составного компаратора.
- Пример:
list.sort(Comparator.comparing(String::length) .thenComparing(Comparator.naturalOrder()));
Здесь сначала строки сортируются по длине, а строки с одинаковой длиной — по алфавиту. - Статические методы
naturalOrder
иreverseOrder
: Comparator.naturalOrder()
возвращает компаратор для сравнения объектов, которые реализуютComparable
.Comparator.reverseOrder()
возвращает компаратор, который сравнивает объекты в обратном порядке.
Пример использования:
List<Integer> numbers = Arrays.asList(5, 3, 8, 1, 9);
numbers.sort((n1, n2) -> n1 - n2); // Лямбда-выражение для сортировки по возрастанию
// Или используя `Comparator` более явно:
numbers.sort(Comparator.naturalOrder());
Comparator
позволяет легко создавать разные способы сортировки объектов без необходимости изменять класс, к которому они принадлежат. Это делает его незаменимым в тех случаях, когда вам нужно гибко управлять сортировкой объектов.
Подводим итоги:
Получилось, конечно, несколько больше чем одна кнопка на чайнике. Но современные чайники тоже развиваются, они меряют температуру внутри себя и умеют подогревать воду не до кипения.
Возможно, что мы увидим все-таки и чайники с JVM внутри, о чем говорилось еще лет 20 назад… Спрошу-ка я у ИИ, как он будет выглядеть.
Помпт был такой:
Нарисуй чайник из будущего с развитым кнопочным интерфейсом.