In Java 8, a functional interface is an interface that has exactly one abstract method. It is also known as a Single Abstract Method (SAM) interface.
Java 8 introduced functional interfaces to enable the use of lambda expressions, which provide a more concise and expressive way to write functional-style code.
To mark an interface as a functional interface, we can use the @FunctionalInterface annotation. While using the annotation is optional, it’s a good practice to add it as it helps to avoid the accidental addition of more abstract methods to the interface, thus preserving its functional nature.
Note – Without a functional interface we can’t use a lambda expression.
Important points related to the functional interface.
- Single Abstract Method (SAM) : A functional interface must have exactly one abstract method. This means the interface can have multiple default or static methods, but it must have only one unimplemented method that represents the primary functionality of the interface.
- Default Method: A functional interface can have multiple default methods, which are methods with implementations provided in the interface itself.
- Static Method: A functional interface can also contain multiple static methods, which are methods declared as static within the interface.
Let’s see an example of a functional interface.
@FunctionalInterface
interface MyFunctionalInterface {
void doSomething(); // Single abstract method
default void doSomethingElse() {
System.out.println("Doing something else...");
}
default void doAnotherThing() {
System.out.println("Doing another thing...");
}
static void staticMethod() {
System.out.println("Static method called.");
}
static void anotherStaticMethod() {
System.out.println("Another static method called.");
}
}
Let’s see examples without using Java 8 Functional interface and with Java 8 functional interface
Example 1 – Use Case: Implementing a custom functional interface to define behavior.
Without using Java 8
// Without Java 8
public class WithoutJava8Example1 {
public static void main(String[] args) {
MyCustomFunctionalInterface myFunc = new MyCustomFunctionalInterface() {
@Override
public void doSomething() {
System.out.println("Doing something!");
}
};
myFunc.doSomething();
}
}
interface MyCustomFunctionalInterface {
void doSomething();
}
With Java 8 Functional interface
@FunctionalInterface
interface MyCustomFunctionalInterface {
void doSomething();
}
// With Java 8
public class WithJava8Example1 {
public static void main(String[] args) {
MyCustomFunctionalInterface myFunc = () -> System.out.println("Doing something!");
myFunc.doSomething();
}
}
Example 2 – Use Case: Using a functional interface for callback mechanism.
Without using Java 8
interface CustomEventListener {
void onEvent(String message);
}
// Without Java 8
public class WithoutJava8Example2 {
public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(new CustomEventListener() {
@Override
public void onEvent(String message) {
System.out.println("Received: " + message);
}
});
}
}
class EventSource {
public void addListener(CustomEventListener listener) {
// Simulate an event occurring
listener.onEvent("Event occurred!");
}
}
With Java 8 Functional interface
// With Java 8
public class WithJava8Example2 {
public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(message -> System.out.println("Received: " + message));
}
}
interface CustomEventListener {
void onEvent(String message);
}
Example 3 – Use Case: Functional interface as an argument in a method.
Without using Java 8
interface MyCustomFunctionalInterface {
void doSomething();
}
// Without Java 8
public class WithoutJava8Example3 {
public static void main(String[] args) {
Processor processor = new Processor();
processor.process(new MyCustomFunctionalInterface() {
@Override
public void doSomething() {
System.out.println("Doing something in Processor!");
}
});
}
}
class Processor {
public void process(MyCustomFunctionalInterface funcInterface) {
funcInterface.doSomething();
}
}
With Java 8 Functional interface
interface MyCustomFunctionalInterface {
void doSomething();
}
// With Java 8
public class WithJava8Example3 {
public static void main(String[] args) {
Processor processor = new Processor();
processor.process(() -> System.out.println("Doing something in Processor!"));
}
}
class Processor {
public void process(MyCustomFunctionalInterface funcInterface) {
funcInterface.doSomething();
}
}
Example 4 – Use Case: Functional interface for strategy pattern.
Without Java 8
// Without Java 8
public class WithoutJava8Example4 {
public static void main(String[] args) {
Context context = new Context(new AddOperation());
System.out.println(context.executeOperation(5, 3)); // Output: 8
context = new Context(new SubtractOperation());
System.out.println(context.executeOperation(5, 3)); // Output: 2
}
}
interface MathOperation {
int operate(int a, int b);
}
class AddOperation implements MathOperation {
@Override
public int operate(int a, int b) {
return a + b;
}
}
class SubtractOperation implements MathOperation {
@Override
public int operate(int a, int b) {
return a - b;
}
}
class Context {
private MathOperation operation;
public Context(MathOperation operation) {
this.operation = operation;
}
public int executeOperation(int a, int b) {
return operation.operate(a, b);
}
}
With Java 8
// With Java 8
public class WithJava8Example4 {
public static void main(String[] args) {
Context context = new Context((a, b) -> a + b);
System.out.println(context.executeOperation(5, 3)); // Output: 8
context = new Context((a, b) -> a - b);
System.out.println(context.executeOperation(5, 3)); // Output: 2
}
}
interface MathOperation {
int operate(int a, int b);
}
class Context {
private MathOperation operation;
public Context(MathOperation operation) {
this.operation = operation;
}
public int executeOperation(int a, int b) {
return operation.operate(a, b);
}
}
Example of Java 8 Functional interface with lambda expression, Method reference, Stream API.
Functional interfaces in Java serve as a foundation for working with lambda expressions and method references, which are powerful features introduced in Java 8 for functional programming. The primary use of functional interfaces is to enable a more concise and expressive way to write code that represents behavior or actions, allowing you to treat functionality as data and pass it around easily. Here are some common use cases of functional interfaces.
- Lambda Expressions: Functional interfaces allow you to define behavior inline using lambda expressions. Instead of creating separate classes or anonymous inner classes to implement an interface, we can directly provide a lambda expression to represent the behavior of the single abstract method of the functional interface.
Use Case 1: Sorting a List of Strings using lambda expressions and Comparator
.
import java.util.Arrays;
import java.util.List;
public class LambdaExpressionsExample1 {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Sorting with lambda expression
names.sort((s1, s2) -> s1.compareTo(s2));
System.out.println(names);
}
}
Output: [Alice, Bob, Charlie, David]
Use Case 2: Calculating the area of a rectangle using lambda expressions and a functional interface.
@FunctionalInterface
interface RectangleAreaCalculator {
double calculateArea(double length, double width);
}
public class LambdaExpressionsExample2 {
public static void main(String[] args) {
RectangleAreaCalculator calculator = (length, width) -> length * width;
double area = calculator.calculateArea(5.0, 3.0);
System.out.println("Area of the rectangle: " + area);
}
}
Output: Area of the rectangle: 15.0
- Method References: Functional interfaces can be used in conjunction with method references to refer to an existing method that matches the functional interface’s method signature. This provides a more elegant way to express the behavior of the functional interface.
Use Case 1: Mapping a list of strings to their lengths using method references.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MethodReferencesExample1 {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// Using method reference
List<Integer> lengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths);
}
}
Output: [5, 3, 7, 5]
Use Case 2: Converting a list of integers to their corresponding strings using method
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MethodReferencesExample2 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Using method reference
List<String> numberStrings = numbers.stream()
.map(Object::toString)
.collect(Collectors.toList());
System.out.println(numberStrings);
}
}
Output: ["1", "2", "3", "4", "5"]
- Stream API and Functional Programming: Functional interfaces play a crucial role in the Stream API introduced in Java 8. Operations such as
map
,filter
,reduce
, and others in the Stream API take functional interfaces as arguments to define the transformation or condition for elements in a stream.
Use Case 1: Filtering even numbers from a list using the Stream API and Predicate
.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamAPIExample1 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Using Stream API and Predicate
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers);
}
}
Output: [2, 4, 6, 8, 10]
Use Case 2: Summing up a list of integers using the Stream API and reduce
.
import java.util.Arrays;
import java.util.List;
public class StreamAPIExample2 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Using Stream API and reduce
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("Sum: " + sum);
}
}
Output: Sum: 15
- Asynchronous Programming: Functional interfaces are used extensively in Java’s CompletableFuture and Future interfaces to define tasks that can be executed asynchronously.
Use Case 1: Using CompletableFuture
for asynchronous execution of tasks.
import java.util.concurrent.CompletableFuture;
public class AsynchronousProgrammingExample1 {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// Simulate a time-consuming task
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});
// Get the result asynchronously
future.thenAccept(result -> System.out.println("Result: " + result));
}
}
Output: Result: 42
(The exact output timing may vary due to the asynchronous nature)
Use Case 2: Using CompletableFuture
to compose asynchronous tasks
import java.util.concurrent.CompletableFuture;
public class AsynchronousProgrammingExample2 {
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 20)
.thenApplyAsync(num -> num * 2)
.thenApplyAsync(num -> num + 10);
// Get the result asynchronously
future.thenAccept(result -> System.out.println("Result: " + result));
}
}
Output: Result: 50
(The exact output timing may vary due to the asynchronous nature)
- Event Handling and Callbacks: Functional interfaces are handy when dealing with event handling and callbacks. Instead of creating custom listener interfaces with multiple methods, you can use a functional interface to define the action to be taken when an event occurs.
Use Case 1: Implementing a custom event listener with a functional interface.
@FunctionalInterface
interface CustomEventListener {
void onEvent(String message);
}
class EventSource {
public void addListener(CustomEventListener listener) {
// Simulate an event occurring
listener.onEvent("Event occurred!");
}
}
public class EventHandlingAndCallbacksExample1 {
public static void main(String[] args) {
// Using the custom event listener
EventSource eventSource = new EventSource();
eventSource.addListener(message -> System.out.println("Received: " + message));
}
}
Output: Received: Event occurred!
Use Case 2: Using Consumer
functional interface for callback functions.
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class EventHandlingAndCallbacksExample2 {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Using Consumer for a callback function
Consumer<String> greetingCallback = name -> System.out.println("Hello, " + name);
names.forEach(greetingCallback);
}
}
Output is
Hello, Alice
Hello, Bob
Hello, Charlie
- Functional Programming Constructs: Functional interfaces are used to define higher-order functions like
Function
,Predicate
,Supplier
,Consumer
, etc., which are essential functional programming constructs in Java.
Use Case 1: Using Function
to transform a list of strings to uppercase.
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class FunctionalProgrammingConstructsExample1 {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Using Function to transform the list to uppercase
Function<String, String> toUpperCaseFunction = String::toUpperCase;
List<String> uppercaseNames = names.stream()
.map(toUpperCaseFunction)
.collect(Collectors.toList());
System.out.println(uppercaseNames);
}
}
Output: ["ALICE", "BOB", "CHARLIE"]
Use Case 2: Using Predicate
to filter even numbers from a list of integers.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class FunctionalProgrammingConstructsExample2 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Using Predicate to filter even numbers
Predicate<Integer> isEven = n -> n % 2 == 0;
List<Integer> evenNumbers = numbers.stream()
.filter(isEven)
.collect(Collectors.toList());
System.out.println(evenNumbers);
}
}
Output: [2, 4, 6, 8, 10]
- Functional Composition: Functional interfaces enable the composition of functions using default methods like
andThen
,compose
, etc. This allows you to chain multiple behaviors together to create more complex functionality.
Use Case 1: Composing two functions using Function
‘s andThen
method.
import java.util.function.Function;
public class FunctionalCompositionExample1 {
public static void main(String[] args) {
Function<Integer, Integer> addOne = num -> num + 1;
Function<Integer, Integer> multiplyByTwo = num -> num * 2;
Function<Integer, Integer> addOneThenMultiply = addOne.andThen(multiplyByTwo);
int result = addOneThenMultiply.apply(5);
System.out.println("Result: " + result);
}
}
Output: Result: 12
Use Case 2: Composing two functions using Function
‘s compose
method.
import java.util.function.Function;
public class FunctionalCompositionExample2 {
public static void main(String[] args) {
Function<Integer, Integer> addOne = num -> num + 1;
Function<Integer, Integer> multiplyByTwo = num -> num * 2;
Function<Integer, Integer> multiplyThenAddOne = addOne.compose(multiplyByTwo);
int result = multiplyThenAddOne.apply(5);
System.out.println("Result: " + result);
}
}
Output: Result: 11
Predefined Java 8 functional interfaces examples
1. Predicate<T>
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
Predicate<Integer> isEven = num -> num % 2 == 0;
System.out.println(isEven.test(4)); // Output: true
System.out.println(isEven.test(7)); // Output: false
}
}
The Predicate
functional interface takes an argument and returns a boolean value. In this example, isEven
is a predicate that checks whether a given integer is even.
2. Function<T, R>
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
Function<Integer, String> intToString = num -> "Number: " + num;
String result = intToString.apply(42);
System.out.println(result); // Output: Number: 42
}
}
The Function
functional interface takes an argument of type T
and returns a result of type R
. In this example, intToString
converts an integer to a string with a prefix.
3. Consumer<T>
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> printMessage = message -> System.out.println(message);
printMessage.accept("Hello, world!"); // Output: Hello, world!
}
}
The Consumer
functional interface takes an argument of type T
and performs some action without returning a result. In this example, printMessage
prints the provided message to the console.
4. Supplier<T>
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get()); // Output: (random double value)
}
}
The Supplier
functional interface takes no arguments and returns a result of type T
. In this example, randomValue
supplies a random double value using the Math.random()
method.
5. UnaryOperator<T>
import java.util.function.UnaryOperator;
public class UnaryOperatorExample {
public static void main(String[] args) {
UnaryOperator<Integer> square = num -> num * num;
System.out.println(square.apply(5)); // Output: 25
}
}
The UnaryOperator
functional interface takes an argument of type T
and returns a result of the same type T
. In this example, square
calculates the square of an integer.
6. BinaryOperator<T>
import java.util.function.BinaryOperator;
public class BinaryOperatorExample {
public static void main(String[] args) {
BinaryOperator<Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 5)); // Output: 8
}
}
The BinaryOperator
functional interface takes two arguments of type T
and returns a result of the same type T
. In this example, sum
calculates the sum of two integers.
These examples demonstrate how to use various predefined Java 8 functional interfaces to achieve different types of operations using lambda expressions.
Questions and answers related to Java 8 functional interface.
1: What is a functional interface in Java 8? Answer: A functional interface in Java 8 has a single abstract method and serves as the basis for lambda expressions.
2: How is a lambda expression related to a functional interface? Answer: Lambda expressions offer a concise way to implement the single method of a functional interface.
3: What is the purpose of the java.util.function
package in Java 8? Answer: The java.util.function
package provides built-in functional interfaces that aid functional programming tasks.
4: What are method references, and how do they relate to functional interfaces? Answer: Method references are shortcuts to refer to methods, often used with functional interfaces as compact alternatives to lambda expressions.
5: What are the key differences between Function
and Consumer
functional interfaces? Answer: Function
takes an input and returns a value, while Consumer
takes an input and performs an action without returning anything.
6: How do you define a custom functional interface? Answer: You define a custom functional interface by declaring a single abstract method within it.
7: What is the purpose of the @FunctionalInterface
annotation? Answer: The @FunctionalInterface
annotation ensures that an interface has only one abstract method, highlighting its functional nature.
8: What does the term “SAM interface” mean in the context of functional interfaces? Answer: “SAM” stands for Single Abstract Method. A SAM interface is a functional interface with only one abstract method.
9: What is the advantage of using method references over lambda expressions? Answer: Method references can lead to more concise and readable code when you’re directly referring to an existing method.
10: How do functional interfaces enhance the Java programming paradigm? Answer: Functional interfaces promote a more modular and expressive code style, enabling the adoption of functional programming concepts in Java.
Check Java 8 functional interface docs.
Other Java 8 examples.
- Difference between Anonymous Inner Class and Lambda Expression
- Java 8 Comparator comparing() example
- Java 8 Lambda Expressions Examples
- Write a program to find the nth Highest Salary using Java…
- Java 8 Functional Interface Examples
- Java 8 Supplier Examples
- Java 8 Consumer Examples
- Java 8 Predicate examples
- Java 8 default methods examples
That’s all about Java 8 Functional Interface Examples.