Functional interfaces are a key feature introduced in Java 8 to support the functional programming paradigm.
A functional interface is an interface that has exactly one abstract method and any number of default or static methods.
Functional interfaces can be used as the target type for lambda expressions and method references, making it easier to express behavior as values.
Java 8 provides a number of predefined functional interfaces in the java.util.function package, which can be used directly or as a basis for creating custom functional interfaces.
Some of the predefined functional interfaces in Java 8 include:
There are many other predefined functional interfaces in the java.util.function package, such as BiFunction<T,U,R>, BiPredicate<T,U>, UnaryOperator<T>, BinaryOperator<T>, and more.
Functional interfaces can be used in a variety of ways, such as:
Filtering collections using predicates,
Transforming objects using functions,
And consuming objects using consumers.
They also enable parallel processing and better code organization, making code easier to read, write, and maintain.
Let's illustrate the use of functional interfaces in Java 8 with some code examples.
Let's start by defining a functional interface called MathOperation that takes two integer parameters and returns an integer result:
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
Note the use of the @FunctionalInterface annotation, which is optional but recommended for clarity.
This annotation indicates that the interface is intended to be used as a functional interface, and the compiler will produce an error if there is more than one abstract method declared in the interface.
Next, let's define a few lambda expressions that implement this MathOperation interface:
/*
* Code example to define and implement functional interface in java
*/
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class JExercise {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
int sum = addition.operate(12,23);
System.out.println("Sum: "+sum);
MathOperation subtraction = (a, b) -> a - b;
int sub = subtraction.operate(12,23);
System.out.println("Subtraction: "+sub);
MathOperation multiplication = (a, b) -> a * b;
int mul = multiplication.operate(12,23);
System.out.println("Multiplication: "+mul);
}
}
Output:
Sum: 35
Subtraction: -11
Multiplication: 276
Note that we are using the lambda expressions as if they were methods, passing in the required parameters and invoking the operate() method.
Another example of functional interfaces in Java 8 is using the Predicate interface to filter a collection based on a certain condition.
Let's say we have a list of strings and we want to filter out all the strings that have a length greater than 5:
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/*
* Code example to define and implement functional interface in java
*/
public class JExercise {
public static void main(String[] args) {
// Creating a list
List<String> strings = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// Getting length of list
Predicate<String> lengthPredicate = s -> s.length() <= 5;
// Filtering elements based on the length
List<String> result = strings.stream().filter(lengthPredicate).collect(Collectors.toList());
// Printing data to console
System.out.println(result); // prints [apple, date]
}
}
Output:
[apple, date]
In this example, we define a lambda expression that implements the Predicate interface and tests if the length of a string is less than or equal to 5. We then use the filter() method of the Stream API to apply this predicate to each element of the strings list and create a new list with only the elements that satisfy the predicate. Finally, we print out the filtered list.
These are just a couple of examples of how functional interfaces can be used in Java 8 to write cleaner and more concise code.
Tere is a direct relationship between lambda expressions and functional interfaces in Java.
Lambda expressions are used to implement functional interfaces, which are interfaces with exactly one abstract method.
The syntax for a lambda expression is similar to the syntax for an anonymous inner class, but much more concise.
Lambda expressions provide a way to define behavior (code) as a value, which can be passed around like any other value.
When a lambda expression is used to implement a functional interface, it provides the implementation for the single abstract method of that interface.
For example, let's consider the Runnable interface in Java, which is a functional interface with a single abstract method named run(). We can create a new thread that runs a Runnable by passing in a lambda expression that implements the run() method:
/*
* Code example to implement functional interface in java
*/
public class JExercise {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("Hello, world!");
});
t.start();
}
}
In this example, the lambda expression () -> { System.out.println("Hello, world!"); } is used to implement the run() method of the Runnable interface.
The Thread constructor takes a Runnable parameter, and we pass in a lambda expression that represents the behavior we want to execute in a new thread.
In summary, lambda expressions are used to implement functional interfaces in Java, providing a way to express behavior as a value. They can be used to create more concise and expressive code, especially when combined with the Stream API and other features of Java 8.