Вы сейчас просматриваете Функциональные интерфейсы – вспомнить всё!

Функциональные интерфейсы – вспомнить всё!

Это просто, как чайник – налил воды, нажал кнопку, получил кипяток.

В Java функциональные интерфейсы являются интерфейсами, которые содержат только один абстрактный метод (но могут иметь несколько статических и/или default методов). Они часто используются в лямбда-выражениях и потоках (streams). В стандартной библиотеке Java уже есть несколько таких интерфейсов:

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

  1. java.lang.Runnable
  • Абстрактный метод: void run()
  • Используется для описания задачи, которая должна быть выполнена в отдельном потоке.
  1. java.util.concurrent.Callable<T>
  • Абстрактный метод: T call() throws Exception
  • Похож на Runnable, но возвращает результат и может выбрасывать исключение.

Интерфейсы из пакета java.util.function:

Пакет java.util.function содержит множество функциональных интерфейсов, которые полезны для работы с лямбда-выражениями и потоками:

  1. Predicate<T>
  • Абстрактный метод: boolean test(T t)
  • Используется для проверки условия (например, фильтрации).
  1. Function<T, R>
  • Абстрактный метод: R apply(T t)
  • Преобразует объект типа T в объект типа R.
  1. Consumer<T>
  • Абстрактный метод: void accept(T t)
  • Принимает объект типа T, но ничего не возвращает (используется для операций, не возвращающих результат, например, печать).
  1. Supplier<T>
  • Абстрактный метод: T get()
  • Не принимает аргументов, но возвращает результат типа T (используется для ленивой генерации данных).
  1. UnaryOperator<T>
  • Наследуется от Function<T, T>.
  • Абстрактный метод: T apply(T t)
  • Преобразует объект типа T в другой объект того же типа.
  1. BinaryOperator<T>
  • Наследуется от BiFunction<T, T, T>.
  • Абстрактный метод: T apply(T t1, T t2)
  • Принимает два аргумента одного типа и возвращает результат того же типа.
  1. BiFunction<T, U, R>
  • Абстрактный метод: R apply(T t, U u)
  • Преобразует объекты типов T и U в объект типа R.
  1. BiConsumer<T, U>
    • Абстрактный метод: void accept(T t, U u)
    • Принимает два аргумента и не возвращает результата.
  2. 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 назад… Спрошу-ка я у ИИ, как он будет выглядеть.

Помпт был такой:

Нарисуй чайник из будущего с развитым кнопочным интерфейсом.

Leonid K

I like Java!