Jan 30, 2020 · by Andreea Avram

Functional Programming in Java: Functional Interfaces

A four-part series dedicated to a deeper dive on the various functional programming concepts

As we already specified in the previous article “Functional Programming in Java: Lambdas,”  lambadas expressions can be used to provide an implementation for interfaces that have only one abstract method and this was the reason that the functional interface concept was introduced in Java.

What is a functional interface?    

A functional interface is an interface annotated with @FunctionalInterface annotation and contains only one abstract method, but the interface can have multiple default methods. Java 8 has a new package “java.util.function” that contains many functional interfaces, the most known and used functional interfaces being Predicate, Consumer, Supplier, Function.

We already know, from the previous article, that lambda expressions are the basis of the other concepts introduced in Java with version 8. In this case, lambda expressions can be used to provide implementations for functional interfaces. Before Java 8 we had to create classes that implemented these interfaces or to use anonymous inner classes.

Runnable interface is one of the most known functional interfaces, this means that it has only one abstract method. Let’s see the following use case of Runnable interface:

public class Test {
    
    public static void main(String arg[]){
        //create anonymous inner class object
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("New thread created");
            }
        }).start();
    }
}

We have created a new thread using a Runnable object, this was possible because we have provided an implementation for Runnable interface using an anonymous inner class.

Because Runnable is a functional interface so has only one abstract method run(), we can create a Thread object using a lambda expression.

//implementation for Runnable interface using lambda expression

new Thread(()->{ System.out.println("thread");}).start();

Usually, a functional interface is annotated with @FunctionalInterface, but any interface that has only one abstract method can be instantiated using lambda expressions. The purpose of this annotation is to inform both the compiler and the developer that the interface has been defined in order to be instantiated using lambda expressions, so it cannot have more than one abstract method.

@FunctionalInterface
public interface MyFunctionalInterface {
    int compute(int x);
}

The new Java package comes with a bunch of built-in functional interfaces, these interfaces are defined for the most commonly used cases.

  • Predicate: has an abstract method test() which returns a boolean value.

    @FunctionalInterface
    public interface Predicate<T> {
    boolean test(T t);
    }
    
  • Function: has an abstract method apply() which takes an argument of type T and returns a result of type R.

    @FunctionalInterface
     public interface Function<T, R> {
    R apply(T t);
    }
    
  • Consumer: has an abstract method accept() which takes an argument of type and does not return any value.

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
    
  • Supplier: has an abstract method get() which takes no argument and returns a result of type T.

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

All the above interfaces are defined using generic types, but what is a generic type? According to official documentation, “A generic type is a generic class or interface that is parameterized over types.”[1] We must mention that a generic type cannot be a primitive type, so because of this condition, generic types generate additional costs caused by Boxing and Unboxing.

To avoid Boxing and Unboxing, Java 8 comes with some primitive functional interfaces. These interfaces are defined using the following primitive types: int, long, and double Depending on the type of input and the type of the returned value we can divide these interfaces into three categories:

  • Interfaces that accept a primitive type and return a reference type: IntPredicate, DoublePredicate, LongPredicate, IntFunction, etc.
  • Interfaces that accept a primitive type and return another primitive type: IntToDoubleFunction, IntToLongFunction etc.
  • Interfaces that accept a reference type and return a primitive type: ToIntFunction,ToLongFunction, ToDoubleFunction etc.

In conclusion, any interface that has only one abstract method (but can have multiple default methods) is a functional interface (@FunctionalInterface annotation is not required). The major benefit is that we can pass a functional interface as an argument to another method and provide an implementation for these interfaces using lambda expressions. Java 8 Collections API has been rewritten and new Stream API is introduced that uses a lot of functional interfaces and Optional class.

In the next article, we will discover what the Optional class brings new to Java and discuss what the most important advantages are of this class.

Sources:

_____________________

To read the first article of this four-part series, click here.

Share This Article

Post A Comment

FacebookInstagramLinkedInTwitterYoutube