- Top 5 Functional Interface Every Java Developer Should Learn
- 5 Essential Functional Interfaces Every Java Developer Should Learn
- 1. The Supplier Interface
- 2. Consumer Interface
- 3. Function
- 4. Predicate
- Функциональные интерфейсы в Java
- Базовые функциональные интерфейсы Java 8
- Predicate
- Consumer
- Supplier
- Function
- UnaryOperator
- Функциональные интерфейсы в Stream
- Метод с Predicate
- Метод с Consumer
- Метод с Supplier
- Метод с Function
- Метод с UnaryOperator
Top 5 Functional Interface Every Java Developer Should Learn
Hello guys, functional interface in Java are an important concept but not many developer pay enough attention to them. They learn lambda and method reference but the functional interface from java.util.function package. While its not really possible to learn all the functional interfaces on that package but you can learn a few more commonly used ones like Predicate, Supplier, Consumer etc and that’s what you will learn in this article. But, before we get to the top 5 functional interfaces that every Java developer should learn, let me tell you a bit about what Java really is.
For those of you who don’t know, Java is basically a general-purpose, class-based, object-oriented programming language that aims to have lesser implementation dependencies. You can also think of Java as a computing platform for application development.
What sets Java apart is that it is fast, secure, and reliable. It can be used for developing Java applications with the help of laptops, data centers, game consoles, scientific supercomputers, and even cell phones.
5 Essential Functional Interfaces Every Java Developer Should Learn
A Java platform is basically a collection of programs that will help you develop and run Java programming applications. It is made up of an execution engine, a compiler, and a set of libraries. It is basically a set of computer software and specifications.
Java was developed by James Gosling when he was working at Sun Microsystems. It was later acquired by the Oracle Corporation.
Java can also be used for developing android applications, creating Enterprise Software, creating mobile java applications, creating scientific computing applications, Big Data analytics, programming hardware devices, and server-side technologies like Apache and GlassFish.
1. The Supplier Interface
The supplier interface has been around since Java 8 and is a part of the java.util.function package. It is used for implementing functional programming in Java. It has a function that does not take any argument but returns a result of the type T.
import java.util.function.Supplier; public class Main < public static void main(String args[]) < // This function returns a random value. Supplier randomValue = () -> Math.random(); // Print the random value using get() System.out.println(randomValue.get()); > >
2. Consumer Interface
The Consumer Interface in Java represents a function that accepts one argument and produces a result. But most of the time, they do not return a value. The consumer interface is made up of two different functions: accept() and andThen()
// Java Program to demonstrate // Consumer's accept() method import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; public class Main < public static void main(String args[]) < // Consumer to display a number Consumer display = a -> System.out.println(a); // Implement display using accept() display.accept(10); // Consumer to multiply 2 to every integer of a list Consumermodify = list -> < for (int i = 0; i < list.size(); i++) list.set(i, 2 * list.get(i)); >; // Consumer to display a list of numbers Consumer
dispList = list -> list.stream() .forEach(a -> System.out.print(a + " ")); List list = new ArrayList(); list.add(2); list.add(1); list.add(3); // Implement modify using accept() modify.accept(list); // Implement dispList using accept() dispList.accept(list); > >
3. Function
A functional interface in Java contains only one abstract method. It can exhibit only one functionality. You can use lambda expressions for representing the instance of a functional interface. It can have a number of default methods.
// Java program to demonstrate Implementation of // functional interface using lambda expressions class Test < public static void main(String args[]) < // lambda expression to create the object new Thread(() -> < System.out.println("New thread created"); >).start(); > >
4. Predicate
The predicate is a functional interface in Java that is defined in the java.util.function package. It can be used for improving the manageability of the code, unit-testing them separately. It also contains a lot of methods.
For example, the isEqual(Object targetRef) method tests if two arguments are equal according to Objects.equals(Object, Object). See the following example to get a better understanding.
Функциональные интерфейсы в Java
Привет! В квесте Java Syntax Pro мы изучали лямбда-выражения и говорили, что это ничто иное как реализация функционального метода из функционального интерфейса. Иными словами, это реализация некоего анонимного (неизвестного) класса, его нереализованного метода. И если в лекциях курса мы углублялись в манипуляции с лямбда-выражениями, сейчас рассмотрим, так сказать, обратную сторону: а именно — эти самые интерфейсы.В восьмой версии Java появилось понятие функциональные интерфейсы . Что же это? Функциональным считается интерфейс с одним не реализованным (абстрактным) методом. Под это определение попадают многие интерфейсы с “коробки”, такие как, например, рассмотренный ранее интерфейс Comparator . А еще — интерфейсы, которые мы создаём сами, как, например:
@FunctionalInterface public interface Converter
@FunctionalInterface public interface Converter < N convert(T t); static boolean isNotNull(T t) < return t != null; >>
@FunctionalInterface public interface Converter < N convert(T t); static boolean isNotNull(T t) < return t != null; >default void writeToConsole(T t) < System.out.println("Текущий объект - " + t.toString()); >>
@FunctionalInterface public interface Converter < N convert(T t); static boolean isNotNull(T t) < return t != null; >default void writeToConsole(T t) < System.out.println("Текущий объект - " + t.toString()); >boolean equals(Object obj); >
И снова компилятор у нас не ругается, поэтому интерфейс Converter всё ещё считается функциональным. Теперь вопрос: а зачем же нам ограничение одним не реализованным методом в функциональном интерфейсе? А затем, чтобы мы могли его реализовать с помощью лямбд. Давайте рассмотрим это на примере Converter . Для этого создадим класс Dog :
Предположим, у нас есть объект Dog , и нам нужно на основе его полей создать объект Raccoon . То есть Converter конвертирует объект одного типа в другой. Как это будет выглядеть:
public static void main(String[] args) < Dog dog = new Dog("Bobbie", 5, 3); Converterconverter = x -> new Raccoon(x.name, x.age, x.weight); Raccoon raccoon = converter.convert(dog); System.out.println("Raccoon has parameters: name - " + raccoon.name + ", age - " + raccoon.age + ", weight - " + raccoon.weight); >
Raccoon has parameters: name - Bobbbie, age - 5, weight - 3
И это значит, что наш метод отработал корректно.
Базовые функциональные интерфейсы Java 8
Ну а теперь рассмотрим несколько функциональных интерфейсов, которые принесла нам Java 8 и которые активно используются в связке со Stream API.
Predicate
Predicate — функциональный интерфейс для проверки соблюдения некоторого условия. Если условие соблюдается, возвращает true , иначе — false :
@FunctionalInterface public interface Predicate
В качестве примера рассмотрим создание Predicate , который будет проверять на чётность числа типа Integer :
public static void main(String[] args) < PredicateisEvenNumber = x -> x % 2==0; System.out.println(isEvenNumber.test(4)); System.out.println(isEvenNumber.test(3)); >
Consumer
Consumer (с англ. — “потребитель”) — функциональный интерфейс, который принимает в качестве входного аргумента объект типа T, совершает некоторые действия, но при этом ничего не возвращает:
@FunctionalInterface public interface Consumer
В качестве примера рассмотрим Consumer , задача которого — выводить в консоль приветствие с переданным строковым аргументом:
public static void main(String[] args) < Consumergreetings = x -> System.out.println("Hello " + x + " . "); greetings.accept("Elena"); >
Supplier
Supplier (с англ. — поставщик) — функциональный интерфейс, который не принимает никаких аргументов, но возвращает некоторый объект типа T:
@FunctionalInterface public interface Supplier
public static void main(String[] args) < ArrayListnameList = new ArrayList<>(); nameList .add("Elena"); nameList .add("John"); nameList .add("Alex"); nameList .add("Jim"); nameList .add("Sara"); Supplier randomName = () -> < int value = (int)(Math.random() * nameList.size()); return nameList.get(value); >; System.out.println(randomName.get()); >
Function
Function — этот функциональный интерфейс принимает аргумент T и приводит его к объекту типа R, который и возвращается как результат:
@FunctionalInterface public interface Function
В качестве примера возьмём Function , который конвертирует числа из формата строк ( String ) в формат чисел ( Integer ):
public static void main(String[] args) < FunctionvalueConverter = x -> Integer.valueOf(x); System.out.println(valueConverter.apply("678")); >
P.S.: если в строку мы передадим не только числа, но и другие символы, вылетит exception — NumberFormatException .
UnaryOperator
UnaryOperator — функциональный интерфейс, принимает в качестве параметра объект типа T, выполняет над ним некоторые операции и возвращает результат операций в виде объекта того же типа T:
@FunctionalInterface public interface UnaryOperator
public static void main(String[] args) < UnaryOperatorsquareValue = x -> x * x; System.out.println(squareValue.apply(9)); >
Мы рассмотрели пять функциональных интерфейсов. Это не все, что доступно нам начиная с Java 8 — это основные интерфейсы. Остальные из доступных — это их усложненные аналоги. Полный список можно посмотреть в официальной документации Oracle.
Функциональные интерфейсы в Stream
Как говорилось выше, эти функциональные интерфейсы плотно связаны со Stream API. Каким же образом, спросите вы?А таким, что многие методы Stream работают именно с данными функциональными интерфейсами. Давайте рассмотрим, как можно применять функциональные интерфейсы в методах Stream .
Метод с Predicate
Для примера возьмем метод класса Stream — filter , который в качестве аргумента принимает Predicate и возвращает Stream только с теми элементами, которые удовлетворяют условию Predicate . В контексте Stream -а это означает, что он пропускает только те элементы, которые возвращают true при использовании их в методе test интерфейса Predicate . Вот как будет выглядеть наш пример для Predicate , но уже для фильтра элементов в Stream :
public static void main(String[] args) < ListevenNumbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8) .filter(x -> x % 2==0) .collect(Collectors.toList()); >
В итоге список evenNumbers будет состоять из элементов <2, 4, 6, 8>. И, как мы помним, collect будет собирать все элементы в некоторую коллекцию: в нашем случае — в List .2,>
Метод с Consumer
Одним из методом в Stream , который использует функциональный интерфейс Consumer , является метод peek . Так будет выглядеть наш пример для Consumer в Stream :
public static void main(String[] args) < ListpeopleGreetings = Stream.of("Elena", "John", "Alex", "Jim", "Sara") .peek(x -> System.out.println("Hello " + x + " . ")) .collect(Collectors.toList()); >
Hello Elena . Hello John . Hello Alex . Hello Jim . Hello Sara .
Но так как метод peek работает с Consumer , модификации строк в Stream не произойдет, а сам peek вернет Stream с изначальными элементами: такими, какими они ему пришли. Поэтому список peopleGreetings будет состоять из элементов «Elena», «John», «Alex», «Jim», «Sara». Также есть часто используемый метод foreach , который аналогичен методу peek , но разница состоит в том, что он конечный — терминальный.
Метод с Supplier
Примером метода в Stream , использующего функциональный интерфейс Supplier , является generate , который генерирует бесконечную последовательность на основе переданного ему функционального интерфейса. Воспользуемся нашим примером Supplier для вывода в консоль пяти случайных имен:
public static void main(String[] args) < ArrayListnameList = new ArrayList<>(); nameList.add("Elena"); nameList.add("John"); nameList.add("Alex"); nameList.add("Jim"); nameList.add("Sara"); Stream.generate(() -> < int value = (int) (Math.random() * nameList.size()); return nameList.get(value); >).limit(5).forEach(System.out::println); >
John Elena Elena Elena Jim
Здесь мы использовали метод limit(5) , чтобы задать ограничение методу generate , иначе программа выводила бы рандомные имена в консоль бесконечно.
Метод с Function
Типичный пример метода в Stream c аргументом Function — метод map , который принимает элементы одного типа, что-то с ними делает и передает дальше, но это уже могут быть элементы другого типа. Как может выглядеть пример с Function в Stream :
public static void main(String[] args) < Listvalues = Stream.of("32", "43", "74", "54", "3") .map(x -> Integer.valueOf(x)).collect(Collectors.toList()); >
Метод с UnaryOperator
- первый — элемент, с которого начинается генерация последовательности;
- второй — UnaryOperator , который указывает принцип генерации новых элементов с первого элемента.
public static void main(String[] args) < Stream.iterate(9, x ->x * x) .limit(4) .forEach(System.out::println); >
То есть каждый наш элемент умножен на самого себя, и так для первых четырёх чисел.На этом всё! Будет отлично, если после прочтения данной статьи вы станете на шаг ближе к пониманию и освоению Stream API в Java!