If one of the standard functional interfaces does the job, you should generally use it in preference to a purpose-built functional interface
This will make your API easier to learn, by reducing its conceptual surface area, and will provide significant interoperability benefits
// Unnecessary functional interface; use a standard one instead
@FunctionalInterface
interface EldestEntryRemovalFunction <K, V> {
boolean remove(Map <K, V> map, Map.Entry <K, V> eldest);
}
Always annotate your functional interfaces with the @FunctionalInterface annotation
@FunctionalInterface annotation is used to ensure that the functional interface can’t have more than one abstract method.
//user defined functional interface
@FunctionalInterface
interface BodyMassIndex
{
double calculate(double weight, double height);
}
//usage
BodyMassIndex bmi = (double weight, double height) -> weight / (height * height);
double result = bmi.calculate(70, 1.7);
System.out.print("\nThe Body Mass Index (BMI) is " + result + " kg/m2");
Remember six basic interfaces. You can derive the rest when you need them
UnaryOperator | BinaryOperator | Predicate | Function <T,R> | Supplier | Consumer |
---|---|---|---|---|---|
T apply(T t) | T apply(T t1, T t2) | boolean test (T t) | R apply(T t) | T get() | void accept(T t) |
Examples
UnaryOperator interface receives a single argument and returns the same value
List<String> names = Arrays.asList("fistik", "kus", "tomis");
names.replaceAll(String::toLowerCase);
BinaryOperator represents an operation upon two operands of the same type, producing a result of the same type as the operands
BinaryOperator<Integer> operator = (x,y) -> x + y;
System.out.println(operator.apply(5, 10));
BinaryOperator<Integer> bi = BinaryOperator.minBy(Comparator.reverseOrder());
System.out.println(bi.apply(2, 3));
BinaryOperator<Integer> bOpertorMax = BinaryOperator.maxBy((Integer t, Integer u) -> t.compareTo(u));
System.out.println(bOpertorMax.apply(10,20));
Predicate represents a predicate (boolean-valued function) of one argument
//simple filter
List<Integer> numbers = Arrays.asList(10, 3, 6, 8 , 11);
List<Integer> filteredNumbers = numbers.stream()
.filter(number -> number.intValue() > 5)
.collect(Collectors.toList());
System.out.println("Predicate - single filter: " + filteredNumbers);
//complex filter
List<Integer> multipleFilters = numbers.stream()
.filter(number -> number.intValue() < 10)
.filter(number -> number.intValue() % 2 == 0)
.collect(Collectors.toList());
System.out.println("Predicate - multiple filters: " + multipleFilters);
//Combining predicates
Predicate<Integer> predicate1 = number -> number.intValue() < 10;
Predicate<Integer> predicate2 = number -> number.intValue() % 2 == 0;
List<Integer> and = numbers.stream()
.filter(predicate1.and(predicate2))
.collect(Collectors.toList());
System.out.println("Predicate - Predicate.and(): " + and);
List<Integer> or = numbers.stream()
.filter(predicate1.or(predicate2))
.collect(Collectors.toList());
System.out.println("Predicate - Predicate.or(): " + or);
List<Integer> negate = numbers.stream()
.filter(predicate1.negate())
.collect(Collectors.toList());
System.out.println("Predicate - Predicate.negate(): " + negate);
Function<T,R> represents a function that accepts one argument and produces a result. This is a functional interface whose functional method is apply(Object).
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add = (value) -> value + 3;
//Returns a composed function that first applies the before function to its input, and then applies this function to the result.
Function<Integer, Integer> addThenMultiply = multiply.compose(add);
//Applies this function to the given argument.
Integer result1 = addThenMultiply.apply(3);
System.out.println("Function: (3 + 3) * 2 " + result1);
//Returns a composed function that first applies this function to its input, and then applies the after function to the result.
Function<Integer, Integer> multiplyThenAdd = multiply.andThen(add);
//Applies this function to the given argument.
Integer result2 = multiplyThenAdd.apply(3);
System.out.println("Function: 3 * 2 + 3 " + result2);
Supplier represents a supplier of results. There is no requirement that a new or distinct result be returned each time the supplier is invoked. This is a functional interface whose functional method is get()
public class Main {
public static void main(String[] args) {
List<String> supplements = Arrays.asList("bcaa", "creatin", "fish oil", "vitamin C");
supplements.stream().forEach(supplement -> {
printSupplements(() -> supplement);
});
}
private static void printSupplements(Supplier<String> supplier){
System.out.println(supplier.get());
}
}
Consumer represents an operation that accepts a single input argument and returns no result. A common example of such an operation is printing where an object is taken as input to the printing function and the value of the object is printed. Unlike most other functional interfaces, Consumer is expected to operate via side-effects. This is a functional interface whose functional method is accept(Object). Since Consumer is a functional interface, hence it can be used as the assignment target for a lambda expression or a method reference
public class Main {
public static void main(String[] args) {
Consumer<String> consumer = Main::printFoods;
consumer.accept("beef");
consumer.accept("brown rice");
consumer.accept("salad");
}
private static void printFoods(String food){
System.out.println(food);
}
}