Functional Interface in Java

 Functional Interface

               Java 8 was released in 2014 and it was one of the major release. Among all other exciting features of Java 8, Lambda Expression is one of the most talked feature. But to use the Lambda Expression we need to know Functional Interface which is another feature introduced in Java 8. As per Java documentation
Lambda expressions let you express instances of single-method classes more compactly

Functional Interface has been introduced in Java to support lambda expression. So before start diving deeper into lambda expression, it is crucial to understand Functional Interface. 

No doubt that interface has gone through a lot of changes in Java 8. Before Java 8, all methods declared in interfaces= are public and abstract by default. But to address the backward compatibility issue, post Java 8 interface can have default methods and static methods. But then what is special about Functional Interface?

What is Functional Interface?

Functional Interface can have only one abstract method and any number of default and static methods. So any interface has single abstract method (SAM) is termed as functional interface. Java also provided @FunctionalInterface annotation to implement functional interface. But is it mandatory to add @FunctionalInterface to implement SAM interface?

Obviously, it is not mandatory. Any interface having single abstract method is considered as functional interface. As per Java documentation
An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. Conceptually, a functional interface has exactly one abstract method. Since default methods have an implementation, they are not abstract. 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 count since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
The purpose of the annotation is to safeguard the contract of functional interface by ensuring single abstract method in the interface. If we do not use the annotation and add multiple abstract methods in interface, it will be considered as a generic interface, but not functional interface. But if we use @FunctionalInterface annotation, compiler will provide compilation error as "Invalid @FunctionalInterface annotation". So it is not mandatory to use annotation to define a functional interface. But it is recommended to use the same to flag fellow developers about the purpose of the interface and avoid human error during development. 

Here is an example of functional interface:

1
2
3
4
5
6
@FunctionalInterface
public interface FirstFunctionalInterface {
	
	void abstractMethod();

}

Now if I try to add multiple abstract methods in the same interface, there will be compilation error.

But if I remove @FunctionalInterface annotation, compiler will consider it as normal interface without any compilation error.

It is also to be noted if an interface declares an abstract method overriding one of the public methods of java.lang.Object, that also does not count as the interface’s abstract method as any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

Why Java introduced Functional Interface?

We could have implemented the interface as classic anonymous class as we used to implement prior to Java 8. But in case of simple interface, the implementation of anonymous class makes over complicated. As per Java documentation

One issue with anonymous classes is that if 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, such as what action should be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.

So functional interface provide a target for Lambda expression as it has single abstract method. If we try to implement Lambda expression for an interface having multiple abstract methods, Lambda expression would implement only one of the abstract methods and leave all other abstract methods unimplemented. That is why Lambda expression can be implemented for functional interfaces. This is actually first step of Java towards functional programming.

Built in Functional Interfaces

Besides introducing the concept of functional interface and providing opportunity to developers to define their own functional interfaces, Java also provided a set of predefined functional interfaces of common uses for the ease of developers. Actually Java introduces a complete new package containing all those functional interfaces as java.util.function.

Some of the most commonly used functional interfaces are:

  • Function
  • Predicate
  • Supplier
  • Consumer

Let's take a deeper look into those functional interfaces.

Function

 Function is one of the simplest form of functional interface and target type for Lambda expression in Java. Function is parameterized by it's argument and return type. It contains single abstract method apply which takes single parameter as argument and returns a value.

1
2
3
4
public interface Function<T,R> {

    R apply(T t);
}  

It contains other methods as well, but they have default implementation. Here is a simple implementation of Function.

1
2
3
Function<Integer, Integer> doubleTheNumber = (num) -> num*2;

System.out.println("Double of 5 is " + doubleTheNumber.apply(5));

Also there are default methods in Function as well.

  • compose allows to combine several functions together and execute sequentially. 
  • andThen allows to apply after function to the output of the function to its input.

Also there is a two-arity specialization of Function. It is called BiFunction which takes two arguments instead of single argument and returns a value.

1
2
3
4
public interface BiFunction<T,U,R> {

    R apply(T t, U u);
}

Some of the specialized forms of Function are DoubleFunction, IntFunction, LongFunction, DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction, IntToLongFunction etc .

Predicate

Predicate is another functional interface which takes an argument and returns true or false. Abstract method of the interface is test which evaluates predicate against it's argument. There are also other default and static methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {
  boolean test(T paramT);
  
  default Predicate<T> and(Predicate<? super T> paramPredicate) {
    Objects.requireNonNull(paramPredicate);
    return paramObject -> (test((T)paramObject) && paramPredicate.test(paramObject));
  }
  
  default Predicate<T> negate() {
    return paramObject -> !test((T)paramObject);
  }
  
  default Predicate<T> or(Predicate<? super T> paramPredicate) {
    Objects.requireNonNull(paramPredicate);
    return paramObject -> (test((T)paramObject) || paramPredicate.test(paramObject));
  }
  
  static <T> Predicate<T> isEqual(Object paramObject) {
    return (null == paramObject) ? Objects::isNull : (paramObject2 -> paramObject1.equals(paramObject2));
  }
}

Typical use case of Predicate is to filter a stream of objects or test a condition against the argument. Let's check a simple example of finding if an integer is even.

1
2
3
Predicate<Integer> isEven = number -> number % 2 == 0;
		
System.out.println("Is 259 even number: " + isEven.test(259));

Other default methods can be used to Predicate chaining. We can combine multiple Predicates using java provided and, or and negate methods.

Some of the specialized forms of Predicate are IntPredicate, LongPredicate and DoublePredicate. Also there is a two-arity specialization of Predicate named BiPredicate which takes two arguments and returns true or false.

Supplier

Supplier is another functional interface which is typically used for lazy generation of values. It has single abstract method get which receives no argument and returns a result. It does not contain any other default or static method.

1
2
3
4
5
6
package java.util.function;

@FunctionalInterface
public interface Supplier<T> {
  T get();
}

Supplier can be used as factory interface to lazily generate a value. Some of the special form of Supplier are IntSupplier , LongSupplier, DoubleSupplier, BooleanSupplier etc.

Consumer

Consumer is yet another functional interface and can be considered as complementary part of Supplier. It has abstract method accept which takes an argument, but does not return anything. Also Consumer interface contains one default method called andThen to perform some operation on the argument. This can be used where an object needs to be consumed and some operation should be performed on that without returning any value.  

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Consumer<T> {
  void accept(T paramT);
  
  default Consumer<T> andThen(Consumer<? super T> paramConsumer) {
    Objects.requireNonNull(paramConsumer);
    return paramObject -> {
        accept((T)paramObject);
        paramConsumer.accept(paramObject);
      };
  }
}

Also there is a two-arity specialization of Consumer named BiConsumer which takes two arguments and return nothing.

Apart from the above mentioned functional interfaces, Java provides lots of other special functional interfaces. Some of other most used function interfaces are BinaryOperator, UnaryOperator etc.

Comments

Popular posts from this blog

HashMap

What is Stream API in Java

HashMap internal working principle