1. Functional Interface
A Functional Interface is an interface that has a maximum of one abstract method and can be implemented using a Lambda Expression.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
2. What are Functional or SAM Interfaces?
An interface with only one abstract method is known as a functional interface. It is also known as a SAM (Single Abstract Method) interface.
The interface represents a function through its single abstract method, which is why it is called a functional interface.
Methods Allowed
- One abstract method.
- Default methods.
- Static methods.
- Overridden methods.
The @FunctionalInterface annotation can be used to declare a Functional Interface.
@FunctionalInterface is used on an interface with more than one abstract method, the compiler reports an error.
Examples Mentioned in the Source
| Interface | Abstract Method |
|---|---|
| Runnable | run() |
| Comparable | compareTo() |
| ActionListener | actionPerformed() |
| Callable | call() |
3. Functional Interfaces in Java
The source lists the following commonly used functional interfaces:
Function<T, R>
Takes one input and produces one result.
BiFunction<T, U, R>
Takes two inputs and produces one result.
Consumer<T>
Consumes one input.
BiConsumer<T, U>
Consumes two inputs.
Supplier<T>
Supplies a value without an input parameter.
Predicate<T>
Tests one input and returns a boolean.
BiPredicate<T, U>
Tests two inputs and returns a boolean.
UnaryOperator<T>
Performs an operation on one value of the same type.
BinaryOperator<T>
Performs an operation on two values of the same type.
| Functional Interface | Generic Form |
|---|---|
| Function | Function<T, R> |
| BiFunction | BiFunction<T, U, R> |
| Consumer | Consumer<T> |
| BiConsumer | BiConsumer<T, U> |
| Supplier | Supplier<T> |
| Predicate | Predicate<T> |
| BiPredicate | BiPredicate<T, U> |
| UnaryOperator | UnaryOperator<T> |
| BinaryOperator | BinaryOperator<T> |
4. Predicate<T>
A Predicate represents a condition that accepts one input and returns a boolean result.
public class PredicateExample {
public static void main(String[] args) {
Predicate<String> isLongerThan5 =
s -> s.length() > 5;
System.out.println(
isLongerThan5.test("Hello")
); // false
System.out.println(
isLongerThan5.test("Functional")
); // true
}
}
5. BiPredicate<T, U>
BiPredicate accepts two inputs and returns a boolean result.
public class BiPredicateExample {
public static void main(String[] args) {
BiPredicate<String, Integer> isLengthEqual =
(str, len) -> str.length() == len;
System.out.println(
isLengthEqual.test("Java", 4)
); // true
System.out.println(
isLengthEqual.test("Spring", 3)
); // false
}
}
6. Function<T, R>
Function accepts one input and produces a result.
public class FunctionExample {
public static void main(String[] args) {
Function<String, Integer> stringLength =
s -> s.length();
System.out.println(
stringLength.apply("Java")
); // 4
System.out.println(
stringLength.apply("Functional")
); // 10
}
}
apply() to execute the function.
7. BiFunction<T, U, R>
BiFunction accepts two input values and produces one result.
public class BiFunctionExample {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add =
(a, b) -> a + b;
System.out.println(
add.apply(10, 20)
); // Output: 30
System.out.println(
add.apply(5, 3)
); // Output: 8
}
}
apply(T, U) accepts two inputs and returns the result.
8. Consumer<T>
Consumer accepts an input and performs an operation without returning a result.
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> greeter =
name -> System.out.println(
"Hello, " + name
);
greeter.accept("Alice");
// Hello, Alice
greeter.accept("Bob");
// Hello, Bob
}
}
accept() and does not return a value.
9. BiConsumer<T, U>
BiConsumer accepts two input values and performs an operation without returning a result.
public class BiConsumerExample {
public static void main(String[] args) {
BiConsumer<String, Integer> printInfo =
(name, age) ->
System.out.println(
"Name: " + name +
", Age: " + age
);
printInfo.accept("Alice", 25);
// Output: Alice is 25 years old.
printInfo.accept("Bob", 30);
// Output: Bob is 30 years old.
}
}
10. BiConsumer with Map
The source demonstrates using a BiConsumer to process the key and value of a Map through Map.forEach().
public class BiConsumerWithMap {
public static void main(String[] args) {
Map<String, Integer> marks =
new HashMap<>();
marks.put("Math", 90);
marks.put("Science", 85);
marks.put("English", 92);
BiConsumer<String, Integer> displayEntry =
(subject, score) ->
System.out.println(
subject + " = " + score
);
marks.forEach(displayEntry);
}
}
11. BiConsumer andThen()
The source demonstrates chaining two BiConsumers using andThen().
public class BiConsumerAndThenExample {
public static void main(String[] args) {
// First BiConsumer: prints name and age
BiConsumer<String, Integer> print =
(name, age) ->
System.out.println(
"Name: " + name +
", Age: " + age
);
// Second BiConsumer: prints a custom message
BiConsumer<String, Integer> greet =
(name, age) ->
System.out.println(
"Hello " + name +
"! You are " + age +
" years young."
);
// Chaining them using andThen()
BiConsumer<String, Integer> combined =
print.andThen(greet);
combined.accept("Alice", 25);
}
}
print executes first, followed by greet.
12. Supplier<T>
Supplier provides a value without taking an input parameter. The source demonstrates a Supplier that generates a random Integer.
public class SupplierExample {
public static void main(String[] args) {
Supplier<Integer> randomSupplier =
() -> new Random().nextInt(100);
System.out.println(
randomSupplier.get()
);
System.out.println(
randomSupplier.get()
);
}
}
get() to obtain a value.
13. UnaryOperator<T>
UnaryOperator is used when the input and output are of the same type.
public class UnaryOperatorExample {
public static void main(String[] args) {
UnaryOperator<String> toUpperCase =
str -> str.toUpperCase();
System.out.println(
toUpperCase.apply("java")
); // JAVA
System.out.println(
toUpperCase.apply("functional")
); // FUNCTIONAL
}
}
14. BinaryOperator<T>
BinaryOperator is used when two inputs and the result are of the same type.
public class BinaryOperatorExample {
public static void main(String[] args) {
BinaryOperator<Integer> add =
(a, b) -> a + b;
System.out.println(
add.apply(10, 20)
); // Output: 30
}
}
15. BinaryOperator with Stream reduce()
The source demonstrates using a BinaryOperator as the accumulator for a Stream reduce() operation.
public class StreamReduceExample {
public static void main(String[] args) {
List<Integer> numbers =
Arrays.asList(1, 2, 3, 4, 5);
BinaryOperator<Integer> sum =
(a, b) -> a + b;
int result =
numbers.stream().reduce(0, sum);
System.out.println(
"Sum: " + result
); // Output: Sum: 15
}
}
16. Quick Revision
| Interface | Input | Output / Purpose | Typical Method |
|---|---|---|---|
| Predicate<T> | 1 | boolean | test() |
| BiPredicate<T,U> | 2 | boolean | test() |
| Function<T,R> | 1 | Result R | apply() |
| BiFunction<T,U,R> | 2 | Result R | apply() |
| Consumer<T> | 1 | No result | accept() |
| BiConsumer<T,U> | 2 | No result | accept() |
| Supplier<T> | 0 | Supplies T | get() |
| UnaryOperator<T> | 1 | Same type T | apply() |
| BinaryOperator<T> | 2 | Same type T | apply() |
Easy Memory Trick
Predicate
Question? → true / false
Function
Transform → input to output
Consumer
Consume → input, no return
Supplier
Supply → no input, returns value
UnaryOperator
One → same input/output type
BinaryOperator
Two → same input/output type