Post

Functional Interfaces in Java

Before moving to examples, do read the blog Functional programming in Java - What and Why? to get more context on what is functional programming.

There are few other ways apart from lambdas, that uses the concept of functional programming and they are:

Functional Interfaces

Functional interfaces is an interface with one abstract method which can be used to implement different use cases. Below are some use cases that might be useful.

Case 1: Single Parameter T and result R

Function<T, R> and apply()

Given a list of names, find the number the names in the list.

1
2
Function<List<String>, Integer> sizeOfList = (value) -> value.size();
logger.debug("Number of names : {}", sizeOfList.apply(names));

Case 1.1: Nested functions

Function<T, R>, andThen and apply()

Given a list of names, find the number the names that starts with A.

1
2
3
4
5
6
7
8
9
10
// Function 1
Function<List<String>, List<String>> startsWithA = (value) -> value.stream()
      .filter(name -> name.startsWith("A")) 
      .collect(Collectors.toList());
// Function 2
Function<List<String>, Integer> sizeOfList = (value) -> value.size();
// Nesting : Function 1 andThen Function 2
Function<List<String>, List<String>> numberOfNamesStartsWithA = startsWithA.andThen(startsWithA);

logger.debug("Number of names that starts with A : {}", numberOfNamesStartsWithA.apply(names);

Case 1.2: Function as parameter and result R

Function<T, R>, compose() and apply()

Given a number, add 10 to it and multiply by 20.

1
2
3
4
5
6
7
// Function 1
Function<Integer, Integer> multiplyBy20 = (value) -> value * 20;
// Function 2
Function<Integer, Integer> add10      = (value) -> value + 10;
// Function 2 instance passed as parameter to Function 1
Function<Integer, Integer> add10ThenMultiplyBy20 = multiplyBy20.compose(add10);
logger.debug(add10ThenMultiplyBy20.apply(30)); // (30 + 10) * 20 = 800

Case 1.3: Two parameters (A and B) and result R

BiFunction<A, B, R> and apply()

Given two numbers, find the sum of the numbers.

1
2
BiFunction<Integer, Integer, Integer> addTwoNumbers = (a, b) -> a + b;
logger.debug(addTwoNumbers.apply(30, 10)); // (30 + 10) = 40

Case 1.4: Three parameters (A, B and C) and result R

TriFunction<A, B, C, R> and apply()

Given three numbers, find the sum of the numbers.

1
2
TriFunction<Integer, Integer> addThreeNumbers = (a, b, c) -> a + b + c;
logger.debug(addThreeNumbers.apply(30, 10, 40)); // (30 + 10+ 40) = 80

Case 2: Single Parameter T and boolean result

Predicate<T> and test()

Given a number, find if the number is greater than 100 or not.

1
2
3
Predicate<Integer> isNumberGreaterThan100 = value -> (value > 100);  
logger.debug(isNumberGreaterThan100.test(10));  // false
logger.debug(isNumberGreaterThan100.test(200));  // true

Case 3: Single Parameter T and no result

Consumer<T> and accept()

Given list of names, display all the names in the list.

1
2
Consumer<List<String>> displayNames = values -> values.stream().forEach(a -> logger.debug("{} ", name));
displayNames.accept(names);

Case 3.1: Nesting of Functions with single Parameter T and no result

Consumer<T>, andThen() and accept()

Given list of names, display all the names in the list followed by the number of names.

1
2
3
Consumer<List<String>> displayNames = values -> values.stream().forEach(a -> logger.debug("{} ", name));
Consumer<Integer> printSizeOfList = values -> logger.debug("Total number of names : {}", values.size);
displayNames.andThen(printSizeOfList).accept(names);

Case 4: No Parameters and result R

Supplier<T> and get()

Print a random number.

1
2
Supplier<Double> findRandomValue = () -> Math.random(); 
logger.debug(findRandomValue.get()); 

Monads

blog post on Monads.


This post is licensed under CC BY 4.0 by the author.