Функциональный интерфейс
Чтобы иметь возможность использовать лямбда выражения, необходим интерфейс:
public interface Searchable
Searchable s = (Car c) -> c.getCostUSD() > 20000;
Лямбда выражения не содержат информацию о том, какой функциональный интерфейс они реализуют.
Тип выражения выводится из контекста, в котором используется лямбда выражение. Этот тип называется целевой тип (target type).
Если лямбда выражение присваивается какому-то интерфейсу, лямбда выражение должно соответствовать синтаксису метода интерфейса.
Одно и то же лямбда выражение может использоваться с разными интерфейсами, если они имеют абстрактные методы, которые совместимы. Например:
interface Searchable < boolean test(Car car); >interface Saleable < boolean approve(Car car); >//. Searchable s1 = c -> c.getCostUSD() > 20000; Saleable s2 = c -> c.getCostUSD() > 20000;
Функциональный интерфейс (functional interface) – это интерфейс у которого только один абстрактный метод. Функциональный интерфейс может содержать любое количество методов по умолчанию (default) или статических методов. Например:
interface A < default int defaultMethod() < return 0; >void method(); >
Еще один пример функционального интерфейса:
interface B < default int defaultMethod() < return 0; >default int anotherDefaultMethod() < return 0; >void method(); >
Функциональный интерфейс может содержать методы класса Object :
interface A
В Java 8 была введена аннотация @FunctionalInterface , которая генерирует ошибку компиляции, если интерфейс не является функциональным:
// This won't compile @FunctionalInterface interface A
Примеры функциональных интерфейсов: java.lang.Runnable , java.util.Comparator.
2. Лямбда vs анонимные классы
Лямбда выражения являются альтернативой анонимным классам. Но они не одинаковы.
- Локальные переменные могут быть использованы только если они final или effective final .
- Разрешается доступ к переменным класса и статическим переменным класса.
- Они не должны выбрасывать больше исключений чем определено в throws метода функционального интерфейса.
- Для анонимных классов ключевое слово this ссылается на сам класс. Для лямбды выражений на внешний класс.
- Анонимные классы компилируются во внутренние классы. Но лямбда выражения преобразуются в статические private методы класса, в котором они используют invokedynamic инструкцию. Лямбда более эффективны, так как не надо загружать еще один класс.
3. Встроенные функциональные интерфейсы
В Java 8 добавлены встроенные функциональные интерфейсы в пакет java.util.function :
Что такое функциональные интерфейсы java
В JDK 8 вместе с самой функциональностью лямбда-выражений также было добавлено некоторое количество встроенных функциональных интерфейсов, которые мы можем использовать в различных ситуациях и в различные API в рамках JDK 8. В частности, ряд далее рассматриваемых интерфейсов широко применяется в Stream API — новом прикладном интерфейсе для работы с данными. Рассмотрим основные из этих интерфейсов:
- Predicate
- Consumer
- Function
- Supplier
- UnaryOperator
- BinaryOperator
Predicate
Функциональный интерфейс Predicate проверяет соблюдение некоторого условия. Если оно соблюдается, то возвращается значение true . В качестве параметра лямбда-выражение принимает объект типа T:
public interface Predicate
import java.util.function.Predicate; public class LambdaApp < public static void main(String[] args) < PredicateisPositive = x -> x > 0; System.out.println(isPositive.test(5)); // true System.out.println(isPositive.test(-7)); // false > >
BinaryOperator
BinaryOperator принимает в качестве параметра два объекта типа T, выполняет над ними бинарную операцию и возвращает ее результат также в виде объекта типа T:
public interface BinaryOperator
import java.util.function.BinaryOperator; public class LambdaApp < public static void main(String[] args) < BinaryOperatormultiply = (x, y) -> x*y; System.out.println(multiply.apply(3, 5)); // 15 System.out.println(multiply.apply(10, -2)); // -20 > >
UnaryOperator
UnaryOperator принимает в качестве параметра объект типа T, выполняет над ними операции и возвращает результат операций в виде объекта типа T:
public interface UnaryOperator
import java.util.function.UnaryOperator; public class LambdaApp < public static void main(String[] args) < UnaryOperatorsquare = x -> x*x; System.out.println(square.apply(5)); // 25 > >
Function
Функциональный интерфейс Function представляет функцию перехода от объекта типа T к объекту типа R:
public interface Function
import java.util.function.Function; public class LambdaApp < public static void main(String[] args) < Functionconvert = x-> String.valueOf(x) + " долларов"; System.out.println(convert.apply(5)); // 5 долларов > >
Consumer
Consumer выполняет некоторое действие над объектом типа T, при этом ничего не возвращая:
public interface Consumer
import java.util.function.Consumer; public class LambdaApp < public static void main(String[] args) < Consumerprinter = x-> System.out.printf("%d долларов \n", x); printer.accept(600); // 600 долларов > >
Supplier
Supplier не принимает никаких аргументов, но должен возвращать объект типа T:
public interface Supplier
import java.util.Scanner; import java.util.function.Supplier; public class LambdaApp < public static void main(String[] args) < SupplieruserFactory = ()->< Scanner in = new Scanner(System.in); System.out.println("Введите имя: "); String name = in.nextLine(); return new User(name); >; User user1 = userFactory.get(); User user2 = userFactory.get(); System.out.println("Имя user1: " + user1.getName()); System.out.println("Имя user2: " + user2.getName()); > > class User < private String name; String getName()< return name; >User(String n) < this.name=n; >>
Введите имя: Том Введите имя: Сэм Имя user1: Том Имя user2: Сэм
Функциональные интерфейсы в Java: Supplier, Consumer, Predicate и Function. Для чего они нужны и как их применять на практике? — Блог
Работаю Java/Kotlin разработчиком в компании Tune-it.
Люблю тёмное Guinness и chocolate trinidad moruga scorpion.
- api (3)
- async (8)
- feature (4)
- framework (5)
- gradle (4)
- java (11)
- json (3)
- kotlin (10)
- nosql (3)
- postgresql (2)
- reactive (8)
- software testing (2)
- spring (8)
- spring boot (7)
- toolkit (3)
- vert.x (3)
- webflux (4)
- асинхронность (8)
- реактивное приложение (8)
- тестирование по (2)
Функциональные интерфейсы в Java 8 → Consumer, Supplier, Predicate и Function. Что к чему и зачем нужны
Java представила поддержку функционального программирования в выпуске Java версии 8. Этот конкретный выпуск также представил несколько новых концепций, в частности лямбда-выражения, ссылки на методы и множество функциональных интерфейсов. При обсуждении последних, есть несколько функциональных интерфейсов, а именно Потребитель (Consumer), Поставщик (Supplier), Предикат (Predicat) и Функция (Function), которые являются наиболее важными. В этой статье мы о них и поговорим.
Consumer (потребитель)
Consumer — это функциональный интерфейс, который принимает один параметр на вход и не возвращает никаких выходных данных. На языке непрофессионала, как следует из названия, реализация этого интерфейса потребляет вводимые данные. Пользовательский интерфейс имеет два метода:
void accept(T t); default Consumer andThen(Consumer after);
Метод accept является единым абстрактным методом (SAM), который принимает один аргумент типа T. Тогда как другой метод andThen является методом по умолчанию и используется для композиции.
Ниже приведен пример интерфейса consumer. Мы создали потребительскую реализацию, которая использует строку, а затем просто выводит ее на экран. Метод forEach принимает реализацию потребительского интерфейса.
public void whenNamesPresentConsumeAll() < ConsumerprintConsumer = t -> System.out.println(t); Stream cities = Stream.of("Sydney", "Dhaka", "New York", "London"); cities.forEach(printConsumer); >
В следующем примере демонстрируется использование составления нескольких реализаций интерфейса consumer для создания цепочки потребителей.
Ниже мы создали двух потребителей: один преобразует список элементов в строки верхнего регистра, а другой выводит строку верхнего регистра.
public void whenNamesPresentUseBothConsumer() < Listcities = Arrays.asList("Sydney", "Dhaka", "London"); Consumer upperCaseConsumer = list -> < for(int i=0; i< list.size(); i++)< list.set(i, list.get(i).toUpperCase()); >>; Consumer printConsumer = list -> list.stream() .forEach(System.out::println); upperCaseConsumer.andThen(printConsumer).accept(cities); >
Интерфейс Consumer имеет специфические типы реализаций для типов integer, double и long -> IntConsumer, DoubleConsumer и LongConsumer, как показано ниже:
IntConsumer void accept(int x); DoubleConsumer void accept(double x); LongConsumer void accept(long x);
Supplier (поставщик)
Supplier — это простой интерфейс, указывающий, что данная реализация является поставщиком какого то результа. Этот интерфейс, однако, не накладывает никаких дополнительных ограничений, которые реализация поставщика должна возвращать при каждом новом получении результата.
У поставщика есть только один метод get() и нет никаких других методов по умолчанию или статических методов.
public void supplier() < SupplierdoubleSupplier1 = () -> Math.random(); DoubleSupplier doubleSupplier2 = Math::random; System.out.println(doubleSupplier1.get()); System.out.println(doubleSupplier2.getAsDouble()); >
Интерфейс поставщик имеет свои примитивные варианты, такие как IntSupplier, DoubleSupplier и т. д., как показано ниже. Обратите внимание, что имя метода — get() используется для универсального интерфейса поставщика. Однако для примитивных вариантов этот метод соответствует примитивному типу.
IntSupplier int getAsInt(); DoubleSupplier double getAsDouble(); LongSupplier long getAsLong(); BooleanSupplier boolean getAsBoolean();
Одно из основных применений этого интерфейса это использование для включения отложенного выполнения. Это означает отсрочку выполнения до тех пор, пока оно не понадобится. Например, в классе Optional есть метод orElseGet. Этот метод срабатывает, если у option нет данных. Это показано ниже:
public void supplierWithOptional() < SupplierdoubleSupplier = () -> Math.random(); Optional optionalDouble = Optional.empty(); System.out.println(optionalDouble.orElseGet(doubleSupplier)); >
Predicate (предикат)
Интерфейс Predicate представляет собой логическую функцию аргумента. Он в основном используется для фильтрации данных из потока (stream) Java. Метод фильтра потока принимает предикат для фильтрации данных и возврата нового потока, удовлетворяющего предикату. У предиката есть метод test(), который принимает аргумент и возвращает логическое значение.
public void testPredicate() < Listnames = Arrays.asList("Smith", "Samueal", "Catley", "Sie"); Predicate nameStartsWithS = str -> str.startsWith("S"); names.stream().filter(nameStartsWithS).forEach(System.out::println); >
В приведенном выше примере мы создали предикат, который проверяет имена, начинающиеся с S. Этот предикат передается потоку.
Predicate также предоставляет несколько стандартных и статических методов для композиции и других целей:
default Predicate and(Predicate other); default Predicate or(Predicate other); static Predicate isEquals(Object targetRef); default Predicate negate();
В следующем примере демонстрируется использование и метод для составления цепочки предикатов.
public void testPredicateAndComposition() < Listnames = Arrays.asList("Smith", "Samueal", "Catley", "Sie"); Predicate startPredicate = str -> str.startsWith("S"); Predicate lengthPredicate = str -> str.length() >= 5; names.stream() .filter(startPredicate.and(lengthPredicate)) .forEach(System.out::println); >
Function (функция)
Интерфейс Function — это более общий интерфейс, который принимает один аргумент и выдает результат. В нем применяется единый абстрактный метод (SAM), который принимает аргумент типа T и выдает результат типа R. Одним из распространенных вариантов использования этого интерфейса является метод Stream.map. Пример использования показан ниже:
public void testFunctions() < Listnames = Arrays.asList("Smith", "Gourav", "John", "Catania"); Function nameMappingFunction = String::length; List nameLength = names.stream() .map(nameMappingFunction).collect(Collectors.toList()); System.out.println(nameLength); >
Подведем итоги
Введение функционального программирования представило новую парадигму языка Java. И интерфейсы Consumer, Supplier, Predicate и Function играют решающую роль в том, как это реализовано в Java. Освоение этих интерфейсов и связанных с ними примитивных вариантов, безусловно, помогает писать более качественный функциональный код.