Создание функционального интерфейса без аргументов в Java 8
Быстрый ответ
Для лямбда-выражений в Java 8, не требующих аргументов и возвращающих значение, подходит интерфейс Runnable
:
Runnable simpleAction = () -> { /* ваш код здесь */ };
simpleAction.run();
Этот код делает то, что от него ожидается, без лишних заморочек.
Если вам приходится работать с интерфейсом, который вы не можете изменить и который требует аргумента, используйте Consumer<Void>
, передавая null
:
Consumer<Void> voidConsumer = v -> { /* здесь ничего не происходит */ };
voidConsumer.accept(null); // Пустое действие.
В случае сомнений лучше выбирать Runnable
для простоты и ясности кода. Consumer<Void>
желательно использовать только в случае крайней необходимости.
Глубже погружаясь в функциональные интерфейсы
Знакомство с функциональными интерфейсами
В java.util.function
выбор инструмента зависит от поставленной задачи:
Runnable
: Идеален при необходимости выполнить действие без входных параметров и возвращаемого значения.Supplier<T>
: Предназначен для возвращения результата без предоставления аргументов.Supplier<String> supplyMessage = () -> "Здесь управляющие команды, аргументы отсутствуют!";
Consumer<T>
: Принимает на вход данные и ничего не возвращает.Consumer<String> consumerPrint = System.out::println; consumerPrint.accept("Печать строки."); // Отправили на печать.
Callable<T>
: ПодобенSupplier
, но может выдать исключение.Callable<String> riskyAction = () -> { throw new IOException("Ошибка исполнения!"); };
Function<T, R>
: Принимает одно значение, преобразует его и возвращает другое.Function<String, Integer> stringToLength = str -> str != null ? str.length() : 0;
UnaryOperator<T>
иBinaryOperator<T>
: Принимают и возвращают аргументы одного и того же типа.UnaryOperator<Integer> squareOper = x -> x * x; // Вычисление квадрата. BinaryOperator<Integer> addOper = Integer::sum; // Вычисление суммы.
Predicate<T>
: Возвразаетtrue
илиfalse
.Predicate<String> notEmpty = s -> s != null && !s.isEmpty();
Особенности использования Void
Void
в Java используется редко, поскольку он практически бесполезен. Для повышения чистоты кода лучше обратить внимание на Runnable
или другие функциональные интерфейсы.
Методы по умолчанию — новые возможности
Методы по умолчанию добавляют новую функциональность в уже существующие интерфейсы, не нарушая их структуру.
Визуализация
Примером может служить игровая ситуация футбольного матча:
Runnable executeKick = () -> System.out.println("Удар по мячу!");
Callable<Void> executeGoal = () -> { System.out.println("Гол!"); return null; };
Удар – простое действие без ожидания результата. Гол – действие с ожидаемым результатом, но неизвестным исходом.
Практическое применение лямбда-выражений
Исключения в лямбда-выражениях
Лямбда-выражения могут выбрасывать проверяемые исключения, требующие обработки при помощи try-catch
или Callable
.
Callable<String> fetchData = () -> {
try {
return obtainData();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
Работа с null
в лямбдах
null
может вызывать исключение NullPointerException
, однако в некоторых случаях его использование в лямбдах оправдано.
Практическое применение методов по умолчанию
Методы по умолчанию позволяют поддерживать код чистым и структурированным.
interface AdvancedRunnable extends Runnable {
default void beforeRun() {
System.out.println("Подготовка к выполнению.");
}
default void afterRun() {
System.out.println("Выполнено. Отдыхай.");
}
}
Полезные материалы
- java.util.function (Java Platform SE 8) – документация по пакету функциональных интерфейсов.
- Lambda Expressions (Учебник Java) – обзор основ лямбда-выражений.
- Uses for the Java Void Reference Type? – обсуждение случаев использования
Void
. - How to Handle Checked Exceptions With Lambda Expression – руководство по обработке исключений в лямбдах.
- Java SE 8: Быстрый старт с лямбда-выражениями – гайд по быстрому старту с лямбда-выражениями.