October 25, 2022

Functional Interface in Java

Functional interface in Java is an interface with a single abstract method. Here is a functional interface example in Java.

interface MyFunctionalInterface{
  void calculateInterest(int i);
}

This interface qualifies as a functional interface because there is a single unimplemented method in the interface.

What qualifies as a functional interface

Java 8 onward an interface can have default methods, static methods and Java 9 onward even private methods, so a functional interface can have these methods too but it should have only a single unimplemented method to be qualified as a functional interface.

If an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count toward the interface's abstract method.

Following interface qualifies as a functional interface even though it contains a default method and a private method as it has a single abstract method.

@FunctionalInterface
interface MyFunctionalInterface{
  void calculateInterest(int i);	
  default void defMethod(){
    commonCode();
    System.out.println("In default method 2");
  }
  private  void commonCode(){
    System.out.println("Executing common code...");
  }
}

Functional interface and Lambda expression

Though there were already interfaces in Java with a single abstract method, like Runnable with its single run() method, Callable with its call() method or Comparator with its compare() method but the term "functional interface" came into fore with the introduction of Lambda expressions in Java 8.

Lambda expression on its own is just a function which needs a target type context to be executed. Since Java is an object oriented language so Lambda expression has to be wrapped with in an object that’s what functional interface provides. Lambda expression specifies the implementation of the abstract method defined by the functional interface and that's how functional interface provides target type for Lambda expression.

Let’s see an example where lambda expression is assigned to a functional interface reference.

//Functional interface
interface MyFunctionalInterface{
  void calculateSimpleInterest(int p, int r, int t);
}
public class LambdaExample {
  public static void main(String[] args) { 
    // lambda assigned to functional interface reference
    MyFunctionalInterface ref = (int p, int r, int t) -> System.out.println("Simple Interest is " + (p*r*t)/100);
    ref.calculateSimpleInterest(10000, 5, 3);
  }
}
Output
Simple Interest is 1500

In the example you can see that the Lambda expression is assigned to MyFunctionalInterface reference. Since target type for lambda expression here is MyFunctionalInterface so an instance of a class is automatically created that implements the functional interface and lambda expression provides implementation of the abstract method declared by the functional interface.

Here is an another example where lambda expression is passed as a method argument. In that case functional interface provides target type as a method parameter.

public class LambdaExample {
  public static void main(String[] args) { 
    new Thread(()->System.out.println("Method argument to Runnable")).start();
  }
}
Output
Method argument to Runnable

Above example is an implementation of Runnable as Lambda expression. As you can see here Thread constructor where Runnable is passed as method parameter is used and Lambda expression implementing the run() method of the Runnable functional interface is passed as method argument. Java run time is able to infer the target type from the context it is used.

@FunctionalInterface annotation in Java

@FunctionalInterface annotation is also added in Java 8 to be used with functional interfaces in Java. By annotating an interface with @FunctionalInterface annotation it is ensured that the interface won’t have more than one abstract method. It is advisable to use it with your functional interface so that no other abstract method is added to the interface even accidentally.

Here is an example of trying to add another abstract method to a functional interface annotated with @FunctionalInterface annotation.

@FunctionalInterface
interface MyFunctionalInterface{
  int calculateSimpleInterest(int p, int r, int t);
  void test(int i);
}

This functional interface gives compile time error “Invalid '@FunctionalInterface' annotation; MyFunctionalInterface is not a functional interface” as there are more than one abstract method.

Pre-packaged Functional interfaces in Java

In the examples shown above we have created our own functional interface but Java comes pre-packaged with many functional interface covering most of the scenarios. A whole new package java.util.function is added in Java 8 containing many functional interfaces to be used off the shelf.

Some of the inbuilt functional interfaces are as follows-

  1. BiConsumer<T,U>- Represents an operation that accepts two input arguments and returns no result.
  2. BiFunction<T,U,R>- Represents a function that accepts two arguments and produces a result.
  3. BinaryOperator<T>- Represents an operation upon two operands of the same type, producing a result of the same type as the operands.
  4. Function<T,R>- Represents a function that accepts one argument and produces a result.
  5. Predicate<T>- Represents a predicate (boolean-valued function) of one argument.
  6. Supplier<T>- Represents a supplier of results.
  7. UnaryOperator<T>- Represents an operation on a single operand that produces a result of the same type as its operand.

Check the whole list here- https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/function/package-summary.html

Example using BiFunction functional interface

Since BiFunction functional interface accepts two arguments and returns a result so it can be used where computation using two arguments is required.

public class LambdaExample {
  public static void main(String[] args) { 
    BiFunction<String, String, String> ref = (str1, str2) -> str1+ " " +str2;
    System.out.println("Concatenating Strings- " + ref.apply("Hello", "Lambda"));
  }
}
Output
Concatenating Strings- Hello Lambda

Example using Predicate functional interface

Predicate functional interface in Java has an abstract method test() which evaluates this predicate on the given argument and return true if the input argument matches the predicate, otherwise false.

Suppose you have a List of Integers and you want to get only those elements of the list which are greater than 10 then you can use Predicate functional interface to test if element is greater than 10 or not and return true only if it is greater than 10.

public class LambdaExample {
  public static void main(String[] args) { 
    List<Integer> myList = Arrays.asList(25, 5, 17, 1, 7, 14, 9, 11);
    LambdaExample obj = new LambdaExample();
    // Lambda expression as method arg
    List<Integer> filterdList = obj.filter(myList, (i) -> i>10);
    System.out.println("Filtered elements- " + filterdList);
  }
	
  public <T> List<T> filter(Collection<T> myList, Predicate<T> predicate) {
    List<T> filterdList = new ArrayList<T>();
    for(T element: myList) {
      if(predicate.test(element)) {
        filterdList.add(element);
      }
    }
    return filterdList;
  }
}
Output
Filtered elements- [25, 17, 14, 11]

In the example lambda expression (i) -> i>10 provides the implementation of the abstract method test() of the Predicate functional interface.

That's all for the topic Functional Interface in Java. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment