JavaJava 8Technology

Java Functional Interfaces Tutorial

A tutorial about Java Functional Interfaces. Learn the concept of Functional Interfaces and why were they added in Java 8 – with the help of code examples.

What is Java Functional Interface ?

Functional Interface is an interface that has just one Abstract method and thus represents a single function contract. In other words, the Functional Interfaces facilitate only a single Function or a method.

Functional Interface is also called as SAM (Single Abstract Method Interface). Thing to note: Although, a Functional Interface can have a Single abstract method. However, it can have any number of Default Methods.

Functional or Not Functional ?

The word Single is not so simple here. Because, the ‘Single’ method can exist in the form of multiple abstract methods that are inherited from super interfaces. But, in that case the inherited methods should logically represent a single method. Alternatively, it might redundantly declare a method that is provided by classes like Object, e.g. toString.

Now, let’s see some of the examples of interfaces and understand if they are Functional.

// Functional
interface Runnable {
    void run();
}


// Not functional; equals is already an implicit member
interface Foo {
    @Override
    boolean equals(Object obj);
}


// Functional; Bar has one abstract non-Object method
interface Bar extends Foo {
    int compare(String o1, String o2);
}


// Functional; Comparator has one abstract non-Object method
interface Comparator {
    boolean equals(Object obj);
    int compare(T o1, T o2);
}


// Not functional; method Object.clone is not public
interface Foo {
    int m();
    Object clone();
}


//------------------------
interface X {
    int m(Iterable arg);
}
interface Y {
    int m(Iterable arg);
}

// Functional: two methods, but they have the same signature
interface Z extends X, Y {}

Annotation @FunctionalInterface

I hope these examples help you understand which Interfaces are actually Functional Interfaces. Alternatively, you can use @FunctionalInterface annotation on the top of an Interface. However, this annotation doesn’t make your interface functional, but throws compilation error if your interface is not a Functional interface.

This annotation is like @Override, which is juts a check and it also improves the code readability.

Functional Interface and Lambda Expressions

In Java 8 and onwards the function interfaces can implemented by the Lambda expressions.

When a method or a expression requires a type Interface which is functional, you can use Lambda syntax to provide inline implementation of the interface.

@FunctionalInterface
public interface Runnable {
   public abstract void run();
}

For example, Runnable class in Java is a Functional Interface. And, below is a traditional way you provide anonymous inner class to provide its implementation.

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("I am running in separate thread");
    }
}).start();

Now below is how Lambda expression can implement the same interface in a short syntax.

new Thread(() -> System.out.println("I am running in separate thread")).start();

In-built Functional Interfaces

Till this point, hope you are clear with the concept of Functional Interfaces and how Lambda expressions implement them. Java has provided some very useful functional interfaces which are ready to use. Instead of creating one, you can use them in many places.

Function

The Function Interface is for applying certain transformation on the given object. Which has a single abstract method called as apply. It can take an argument of a type and can return other type.

public interface Function<T,U> {
    public <U> apply(T parameter);
}

For example, java’s Stream.map accepts a Function implementation. Firstly, we will see an example of Anonymous implementation.

employees.stream().map(new Function<Employee, String>() {
       @Override
       public String apply(Employee e) {
           return e.getName();
       }
}).collect(Collectors.toList());

With lambda the above statement look a lot readable and simpler.

employees.stream()
    .map(x -> x.getName())
    .collect(Collectors.toList());

The syntax can more be simplified using the Method Reference.

employees.stream()
    .map(Employee::getName)
    .collect(Collectors.toList());

To sum up, Function interface can be used where an object or a value is being transformed – like the map method above – where the Stream of Employees is then mapped into Stream of Strings..

Consumer

This is one more pre-defined Functional Interface. As the name suggest it defines a function which consumes the given parameter.

public interface Consumer <T> {
    void accept(T t);
}

For example, Stream.forEach. Which is called once per element in the Stream and returns void. Let’s see how we can use Consumer implementation here.

employees.stream()
    .map(Employee::getName)
    .forEach(System.out::println);

The Stream of Employee is first mapped to Stream of Strings (employee names). After that each name is printed inside the forEach method.

Predicate

The Predicate represents a function that evaluates the state of an object into Boolean value. The function accepts an object and returns boolean.

public interface Predicate {   boolean test(T t); }

For example, we can refer to Stream.filter method, which is used to filtering out elements from the stream.

employees.stream()
    .filter(e -> e.getAge() >= 40)
    .collect(Collectors.toList());

Here, the filter method is filtering out employees ageing over 40 and collecting the rest in a list.

Supplier

The Supplier interface is to supply things. The Supplier function doesn’t accept any argument but can return an object of provided generic type.

public Interface Supplier<T>{
    T get();
}

You cannot re-use java Streams.. In other words, you call a Terminal Operation on a stream the stream is dead.

Stream<Employee> empStream = Stream.of(new Employee("a", 43), new Employee("b",39));

// Terminal Operation is Called on the Stream
empStream.filter(emp -> emp.getAge() >= 40).forEach(System.out::println);

//Using same stream results in Runtime Exception
//Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

empStream.forEach(System.out::println);

In such cases, supplier helps. Also it is very useful where you want to create reusable data-set. For example, mock datasets in tests.

Supplier<Stream<Employee>> supplier = () -> {Stream.of(new Employee("a", 43), new Employee("b", 39)};
        
supplier.get()
    .filter(emp -> emp.getAge() >= 40)
    .forEach(System.out::println);


supplier.get()
    .forEach(System.out::println);

In the above example, both of the operations are possible. Because every time you use the Supplier as new Stream is created.

Binary Operator

This BinaryOperator interface represents a function which takes to parameters and returns one. You can use it to define a Mathematical operations like comparison, addition etc.

For example, Java Stream.reduce method takes BinaryFunction. Using reduce, we will find youngest employee in the stream.

empStream
    .reduce((x, y) -> x.getAge() <= y.getAge() ? x : y)
    .ifPresent(System.out::println);
Unary Operator

The UnaryOperator interface defines a function that takes one parameter and return an object of same time. You can use this function to change the value of given objet. For example, finding square of a number or converting String to upper case.

List<Double> longs = Arrays.asList(1d, 2d, 3d, 4d, 5d);
//square of each number in the list
longs.replaceAll(l -> Math.sqrt(l));
//Or, using method reference
longs.replaceAll(Math::sqrt);

Additionally, we will see an example of generating infinite stream of sequential numbers using Stream.iterate method which accepts a UnaryOperator. We will print only first 10 elements from the stream.

Stream
    .iterate(1, x -> x + 1)
    .limit(10)
    .forEach(System.out::println);

Summary

This was Java Functional Interfaces Tutorial. Where, you learnt that Functional Interfaces have Single Abstract Method (SAM). They represent a single functional contract.

The Java 8 Lambda Expressions provide in-line implementations for the functional interfaces. Moreover, these in-line implementations are short and simpler compared to the anonymous implementations. Also, you learnt some of the in-built functional interfaces by java, and you can re-use them in variety of situations.