Java is based on OOPS concepts and abstraction is one of its feature. So by using interfaces in Java, we achieve abstraction. Like a class, an interface can have methods and variables, but the methods declared in an interface are by default abstract (method signature without body).
What is Functional Interface
A function Interface is an interface which contains only one abstract method. They can have only single functionality to exhibit. From Java 8 onwards, a lambda expression can be used to represent the instance of a functional interface. We will see lambda expressions later on this blog. @FunctionalInterface
annotation is added so that we can mark an interface as a functional interface.
//declaration of functional interface
@FunctionalInterface
interface Square {
int calculate(int x);
}
@FunctionalInterface annotation is not mandatory to use, but it consider as a best practice to use it with functional interfaces. It avoids the addition of extra abstract methods added accidentally. If the interface is annotated with @FunctionalInterface and we try to have more than one abstract method, it throws a compile-time error.
Why Functional Interface?
The biggest benefit of the functional interface is to use the lambda expression and avoid to write complex anonymous classes. Lambda expression has its own benefits in term of functional programming language.
What is Lambda Expression
Lambda expressions basically express instances of the functional interfaces. It implements the only abstract function. Suppose you have an anonymous class and the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In these cases, you’re usually trying to pass functionality as an argument to another method, Lambda expressions enable you to do this, to treat functionality as a method argument.
//Syntex for using lambda expression
functionalInterface_Name ref = (argument list) -> { body }
Different lambda expression scenarios.
- with no parameter
//with no parameter
() -> System.out.println("Zero parameter lambda");
- with one parameter
//with one parameter you may or may not be use parentheses
//with parentheses
(p) -> System.out.println("One parameter: " + p);
//without parentheses
p -> System.out.println("One parameter: " + p);
- multiple parameter
//multiple parameter
(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
Advantages of Lambda expression
- Reduce the line of code.
// Without Using Lambda Expression
Runnable task1 = new Runnable() {
@Override
public void run() {
System.out.println("Task is running");
}
};
// Using Lambda Expression
Runnable task1 = ()-> System.out.println("Task is running");
- Passing behavior as an argument into methods.
@FunctionalInterface
interface Calculate {
int sum(int x, int y);
}
public class Addition {
private static void engine(Calculate cal) {
int x = 2, y = 4;
System.out.println(cal.sum(x, y));
}
public static void main(String[] argv) {
Calculate.engine((x, y) -> x + y);
}
}
- Lambda allow Java to be lazy.
Lambda allows Java to be lazy, as they represent a function to be executed that can be passed around and will only be evaluated when required. Let understand this with an example. We need to write a method to find the square root of the largest element of an array list.
import java.util.*;
import java.util.stream.*;
public class NumberTest {
public static int findSqrOfLargest(List<Integer> number) {
int max = 0;
for(int num : number){
if(num > max){
max = num;
}
}
return max * max;
}
public static void main(String[] args) {
List<Integer> number = Arrays.asList(2,3,4,5,6,7,1);
System.out.println(NumberTest.findSqrOfLargest(number));
}
}
OUTPUT:
49
Above program will always run in sequential order but we can achieve the same functionality by streams API and use the benefit of Laziness.
//Using Stream API
import java.util.*;
import java.util.stream.*;
public class NumberTest {
public static int findSqrOfLargestByLaziness(List<Integer> number)
{
return number.stream()
.max(Integer::compare)
.map(n -> n*n).get();
}
public static void main(String[] args) {
List<Integer> number = Arrays.asList(2,3,4,5,6,7,1);
System.out.println(NumberTest.findSqrOfLargestByLaziness(number));
}
}
OUTPUT:
49
- Used In Sequential and Parallel Execution.
//Sequential Execution
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> even = numbers.stream()
.filter(numb -> num % 2 == 0)
.map(num -> num * num)
.collect(Collectors.toList());
“Stream” use single thread when performing operations sequentially. In the above snippet.
- .filter extracts those elements from the list, which are even.
- .map performs square on the filtered elements.
- .collect will store the resultant list into the list i.e. “even”.
//parallel Execution
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> even = numbers.parallelStream()
.map(num -> num * num)
.filter(numb -> num % 2 == 0)
.collect(Collectors.toList());
In the above snippet parallel “Stream” perform the same operation but use more than one thread. Parallel and sequential both streams use lambda expressions.
Hope, I have cleared your queries regarding functional interface and lambda expression. If you want to learn more about it you can refer to some references, provided below.
References
- https://www.javatpoint.com/java-8-functional-interfaces
- https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
- https://www.geeksforgeeks.org/functional-interfaces-java/