Functional Interfaces in Java
Functional Interface(FI) refers to a special kind of interface, it's an interface with only one abstract method, that is, FI can have many other implemented methods, but only one abstract method.
package com.sosv.functional.generic;
public interface GenericInterface {
String numberToText(String lang, Long number);
default long countDigits(Long number) {
return (long)(Math.log10(number) + 1);
}
}
We can annotate with @FunctionalInterface an interface to validate that interface has only one abstract method.
@FunctionalInterface
public interface GenericInterface {
String numberToText(String lang, Long number);
default long countDigits(Long number) {
return (long)(Math.log10(number) + 1);
}
}
Before FI, if you want to create an instance of an interface, you can create a new class that implements the interface or create an anonymous class.
For example:
//Create new Class
public class GenericClass implements GenericInterface {
@Override
public String numberToText(String lang, Long number) {
return "";
}
}
var generic = new GenericClass();
//Annonymous class
var generic = new GenericInterface() {
@Override
public String numberToText(String lang, Long number) {
return "";
}
};
Lambda Expressions(LE)
Since java SE 8, In addition to FI, we have lambda expressions. For me, LE is an implementation of a FI. We can reduce the above verbose code into cleaner code, easy to read.
GenericInterface generic = (String lang, Long number) -> "";
The basic notation for a lambda expression is:
(<parameters>) -><arrow operator> <body>
parameters: It can be 0, 1, or N parameters, you can specify the type of all parameters or neither parameter. Ex: (String lang, Long number) or (lang, number)
arrow operator: It's a "-", followed by ">". Ex: ->
body: It can have {} or not. depends on the implementation of the lambda expression.
If body doesn't have {}, return keyword is implicit;
{} it's mandatory when the abstract method doesn't have a return type, that is, void.
Ex: "" or {return "";}
Kinds of FI
We have basically 4 types of FI, and all of these FI are in java.util.function package.
FI | Input | Return |
Predicate | (T param) | Boolean |
Function | (T param) | (R response) |
Supplier | no param | (R response) |
Consumer | (T param) | no response |
T, R: Generic.
//Predicate
Predicate<String> hasNumber = (text) -> text.matches(".*\\d+.*");
//Function
Function<Integer, Integer> square = (num1) -> num1 * num1;
//Supplier
Supplier<Double> random = () -> Math.random();
//Consumer
Consumer<String> logger = (text) -> System.out.println(text);
Operations of FI
//Predicate
Predicate<String> hasNumber = (text) -> text.matches(".*\\d+.*");
Predicate<String> isLargeText = (text) -> text.length() > 500;
hasNumber.negate() // or (text) -> !text.matches(".*\\d+.*");
hasNumber.and(isLargeText) // or (text) -> text.matches(".*\\d+.*") && text.length() > 500
hasNumber.or(isLargeText) // or (text) -> text.matches(".*\\d+.*") || text.length() > 500
hasNumber.test("Hello") //To get result of predicate
//Function
Function<Integer, Integer> square = (num1) -> num1 * num1;
Function<Integer, Integer> triple = (num1) -> num1 * 3;
square.andThen(triple) // or num*num*3
triple.andThen(square) // or (3*num)*(3*num)
triple.apply(2) // return 6
//Supplier
Supplier<Double> random = () -> Math.random();
random.get() // to get value
//Consumer
Consumer<String> logger = (text) -> System.out.println(text);
Consumer<String> greetings = (name) -> System.out.println("Hello " + name);
logger.andThen(greetings) // or print text and "Hello" + text
logger.accept("Hashnode") // to execute consumer
I hope you enjoy this summary of FI and LE.