{{announcement.body}}
{{announcement.title}}

Be More Functional: Java's Functional Interfaces

DZone 's Guide to

Be More Functional: Java's Functional Interfaces

Learn more about popular functional interfaces in Java, including Consumer, Function, and Predicate.

· Java Zone ·
Free Resource
Learn more about popular functional interfaces in Java, including Consumer, Function, and Predicate.

Hey tea lovers, this post is about the functional interfaces provided by Java. We will talk about the basic ones briefly. These functional interfaces are used by the Streams API heavily, so knowing them will make your life easier. Not just streams you can use it anywhere unless you want to. You can find the code on GitHub and the full project here.

Prerequisites

Just make sure you have a solid understanding of functional interfaces and lambdas. This post is dependent on these concepts.

java.util.function

The functional interfaces we will be discussing in your tea break will come from java.util.function. This package contains Java’s general-purpose functional interface. This helps us to save time by not writing it repeatedly.

It has many functional interfaces (you probably won’t be needing that many in your code), but we are going to see the most common and useful functional interfaces. We will be comparing it to the normal method or function for better comprehension.

Consumer <T>

As the name implies Consumer consumes inputs. It doesn’t return anything when called. It is similar to a function with the void return type. This functional interface has two methods, void accept(T t) that triggers, which triggers the interface, and andThen(Consumer<T>), which lets you chain multiple Consumers.

You can call multiple Consumers with andThen(consumer1).andThen(consumer2)..... and call the accept(t) function to terminate the chain. All Consumers in this chaining will use the same input given via accept().

Java




x
21


 
1
//define the consumer which consumes the age to print it
2
 
          
3
Consumer<Integer> printAgeConsumer = new Consumer<Integer>() {
4
    @Override
5
    public void accept(Integer age) {
6
        System.out.println("Age is " + age);
7
    }
8
};
9
 
          
10
//call the method
11
printAgeConsumer.accept(23);// Age is 23
12
//using lambda
13
Consumer<Integer> printAgeWithLamda = (age) -> System.out.println("Lamdda : age is " + age);
14
//will work similar as printAgeConsumer
15
printAgeWithLamda.accept(223);//Lamda : age is 223
16
 
          
17
//chaining with andThen(Consumer)
18
printAgeConsumer //1st
19
        .andThen(printAgeWithLamda)//2nd
20
        .andThen(age -> System.out.println("How old is he ? " + age))//3rd
21
        .accept(23);//this value will be given to each consumer



You may also like: The Best of Java Collections [Tutorials].

Predicate <T>

The Predicate is used for boolean values. It tests the given value and returns a boolean by calling test(T t). Then, again, like Consumer, you can use chaining withand(anotherPredicate). When calling test(T) in the end, we will complete the chain. Similar to Consumer, they use the same input throughout the chain. It returns true only if all the predicates return true; otherwise, it returns false.

Java




xxxxxxxxxx
1
19


 
1
Predicate<Integer> isEven = new Predicate<Integer>() {
2
    @Override
3
    public boolean test(Integer number) {
4
        return number % 2 == 0;
5
    }
6
};
7
boolean is23Even = isEven.test(23);//false
8
 
          
9
//using lambda
10
Predicate<Integer> isEvenlambda = (number) -> number % 2 == 0;
11
boolean is46Even = isEvenlambda.test(46);//true
12
 
          
13
//chaining with and()
14
//returns true only of all the predicated returns true else false
15
isEven // 1st - number should be even
16
        .and(n -> n < 10)//2nd -number should be less than 10
17
        .and(n -> n > 5) // 3rd should be greater than 5
18
        .test(6); // this number will be used in chain
19
// returns true after completion



Function <T, R>

Yes, that’s the name. Unlike preceding interfaces, Function has two generic parameters. The first parameter is for input type, and the second one is for the return type. Essentially, Function is used for transforming a given value into a desired one.

Functions uses apply(T t) for its functions and andThen(anotherFuntion) for chaining. But, dissimilar to  Consumer and Predicate, this chaining does not use the same value to all the Functions. Instead, they use the result of the preceding Function. However, keep in mind that the subsequent Function has the input type equivalent to the current Function’s return type.

Java




xxxxxxxxxx
1
18


 
1
Function<Integer, Integer> doubleTheNumber = new Function<Integer, Integer>() {
2
    @Override
3
    public Integer apply(Integer number) {
4
        return number * 2;
5
    }
6
};
7
int doubleTheNumber2 = doubleTheNumber.apply(2);//4
8
//using lambda
9
Function<Integer, Integer> subtract2 = (number) -> number - 2;
10
int subtract2From4 = doubleTheNumber.apply(4);//2
11
 
          
12
//chaining with andThen()
13
//Unlike Predicate and Consumer which uses the same value to all the nodes,
14
//Function uses the result of previous Function
15
doubleTheNumber // 1st will double the number
16
        .andThen(subtract2) // 2nd will subtract 2 from doubled number
17
        .andThen(doubleTheNumber) // 3rd subtracted result will be doubled
18
        .apply(4); // (((4 * 2) - 2) * 2) = 12



BiFunction <T, U, V>

BiFunction is similar to  Function, except this accepts two input parameters as opposed to  Function , which handles only one input. It uses apply(T t, U u) for triggering. Chaining in this uses Function instead of BiFuntion in andThen(Function<V v, E e>), since methods don’t have two return values.

Java




xxxxxxxxxx
1
17


 
1
BiFunction<Integer, Integer, Integer> areaOfRectangle = new BiFunction<Integer, Integer, Integer>() {
2
    @Override
3
    public Integer apply(Integer length, Integer breadth) {
4
        return length * breadth;
5
    }
6
};
7
 
          
8
areaOfRectangle.apply(2, 2);//4
9
 
          
10
BiFunction<Integer, Integer, String> areaWitMessage = (lengh, breadth) -> "Area is " + lengh * breadth;
11
 
          
12
areaWitMessage.apply(4, 3);// Area is 12
13
 
          
14
//chaining
15
areaOfRectangle // 1st area will be calulated
16
        .andThen(area -> area * 3) // 2nd calulated area will be multiplied by 3 as height
17
        .apply(2, 3);// 2 * 3 * 3 = 18



Supplier < T >

Until now, we have talked about the interfaces that take input parameters. Supplier on the other hand, does not demand any arguments; it just produces one by calling get(), similar to methods that produce values without input, like toString() or hashCode(). We can’t use chaining in this since it does not take input parameters.

Java




xxxxxxxxxx
1
12


 
1
Supplier<Double> randomSupplier = new Supplier<Double>() {
2
    @Override
3
    public Double get() {
4
        return Math.random();
5
    }
6
};
7
double randomNumber = randomSupplier.get();
8
System.out.println(randomNumber);
9
//lambda
10
Supplier<String> codersTeaUrl = () -> "https://coderstea.com";
11
System.out.println(codersTeaUrl.get());
12
 
          



Wait, There's More!

There are more Functional interfaces in the package. They are the implementation of these above functional interfaces for specific challenges or usage. We don’t have to understand every functional interface. If you understand the interfaces we discussed, you are good to go. Let's look at them briefly.

The following sample table is from the Java doc. You can find a full table here or here.

Interface Description
BiConsumer<T,U> Represents an operation that accepts two input arguments and returns no result.
BiFunction<T,U,R> Represents a function that accepts two arguments and produces a result.
BinaryOperator<T> Represents an operation upon two operands of the same type, producing a result of the same type as the operands.
BiPredicate<T,U> Represents a predicate (boolean-valued function) of two arguments.
BooleanSupplier Represents a supplier of boolean-valued results.
Consumer<T> Represents an operation that accepts a single input argument and returns no result.
DoubleBinaryOperator Represents an operation upon two double-valued operands and producing a double-valued result.
DoubleConsumer

Represents an operation that accepts a single

double

-valued argument and returns no result.


That's it for this post. You can find the code on GitHub here or the full project here.

Further Reading

Topics:
java ,functional interfaces ,functional programming ,interface ,lambda

Published at DZone with permission of Imran Shaikh . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}