Java 8 Functional Interfaces

In this post, we will learn the Java 8 functional interface with examples.
The source code of this tutorial is hosted on my GitHub repository at Java 8 Functional Interfaces Source Code.

Key points about the functional interface:

  1. An Interface that contains exactly one abstract method is known as a functional interface.
  2. It can have any number of default, static methods but can contain only one abstract method. It can also declare the methods of the object class.
  3. Functional Interface is also known as Single Abstract Method Interfaces or SAM Interfaces. It is a new feature in Java 8, which helps to achieve a functional programming approach.
  4. A functional interface can extend another interface only when it does not have any abstract method.
  5. The Java API has many one-method interfaces such as Runnable, Callable, Comparator, ActionListener, and others. They can be implemented and instantiated using anonymous class syntax.

Examples of Custom Functional Interface

Create a custom Sayable interface is a functional interface annotated with @FunctionalInterface.
The @FunctionalInterface annotation indicates that an interface is a functional interface and contains exactly one abstract method.

Custom Functional Interface Example

Let's create a Sayable interface annotated with @FunctionalInterface annotation.
@FunctionalInterface  
interface Sayable{  
    void say(String msg);   // abstract method   
}  
Let's demonstrate a custom functional interface via the main() method. We use Lambda expression to implement functional interfaces:
public class FunctionalInterfacesExample {

    public static void main(String[] args) {

        Sayable sayable = (msg) -> {
            System.out.println(msg);
        };
        sayable.say("Say something ..");
    }
}

Java 8 Predefined-Functional Interfaces

Java 8 provides predefined functional interfaces to deal with functional programming by using lambda and method references.
Let's discuss  Java Predefined-Functional Interfaces with examples.
Let's first create a Person class, we use this Person class is used to demonstrate Predefined-Functional Interfaces examples.
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Predicate Functional Interface

We need a function for checking a condition. A Predicate is one such function accepting a single argument to evaluate a boolean result.

It has a single method test that returns the boolean value.
Predicate interface from JDK 8: Let's look at the internal implementation of the Predicate interface. Predicate interface contains exactly one abstract method test(T t). Note that it also contains default, static methods.
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

   static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
Let's create an example to demonstrates the usage of the Predicate functional interface:
public class PredicateExample {
    public static void main(String[] args) {
        Predicate < Person > predicate = (person) -> person.getAge() > 28;
        boolean result = predicate.test(new Person("ramesh", 29));
        System.out.println(result);
    }
}

Function Functional Interface

It represents a function that accepts one argument and returns a result.
Function interface from JDK 8: Let's look at the internal implementation of the Function interface. The function interface contains exactly one abstract method apply(T t). Note that it also contains default, static methods.
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

   
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
Let's create an example to demonstrates the usage of the Function functional interface:
import java.util.function.Function;

public class FunctionExample {

    public static void main(String[] args) {
        // convert centigrade to fahrenheit
        Function < Integer, Double > centigradeToFahrenheitInt = x -> new Double((x * 9 / 5) + 32);

        // String to an integer
        Function < String, Integer > stringToInt = x -> Integer.valueOf(x);
        System.out.println(" String to Int: " + stringToInt.apply("4"));


        Function < PersonEntity, PersonDTO > function = (entity) -> {
            return new PersonDTO(entity.getName(), entity.getAge());
        };
        PersonDTO personDTO = function.apply(new PersonEntity("ramesh", 20));
        System.out.println(personDTO.getName());
        System.out.println(personDTO.getAge());
    }
}

class PersonEntity {
    private String name;
    private int age;

    public PersonEntity(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class PersonDTO {
    private String name;
    private int age;

    public PersonDTO(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Supplier Functional Interface

Represents a supplier of results.
Supplier interface from JDK 8: Let's look at the internal implementation of the Supplier interface. The supplier interface contains exactly one abstract method get(T t). Hence we can apply lambda expression to it.
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Let's create an example to demonstrates the usage of the Supplier functional interface:
import java.util.function.Supplier;

public class SuppliersExample {

   public static void main(String[] args) {
  
       Supplier<Person> supplier = () -> { 
           return new Person("Ramesh", 30 );
       };
  
       Person p = supplier.get();
       System.out.println("Person Detail:\n" + p.getName() + ", " + p.getAge());
   }
}

Consumer Functional Interface

It represents an operation that accepts a single argument and returns no result.
Consumer interface from JDK 8: Let's look at the internal implementation of the Consumer interface. The consumer interface contains exactly one abstract method accept(T arg0). Hence we can apply lambda expression to it.
@FunctionalInterface
public interface Consumer<T> {
    void accept(T arg0);

    default Consumer<T> andThen(Consumer<? super T> arg0) {
       Objects.requireNonNull(arg0);
       return (arg1) -> {
       this.accept(arg1);
       arg0.accept(arg1);
    };
  }
}
Let's create an example to demonstrates the usage of the Consumer functional interface:
public class ConsumersExample {

    public static void main(String[] args) {
        List<Person> listOfPerson = new ArrayList<Person>();
        listOfPerson.add(new Person("abc", 27));
        listOfPerson.add(new Person("mno", 26));
        listOfPerson.add(new Person("pqr", 28));
        listOfPerson.add(new Person("xyz", 27));

        listOfPerson.forEach((person) -> {
            System.out.println(" Person name : " + person.getName());
            System.out.println(" Person age : " + person.getAge());
        });
  
  
        // Second example
        Consumer<Person> consumer = (person) -> {
           System.out.println(person.getName());
           System.out.println(person.getAge());
        };
        consumer.accept(new Person("Ramesh", 30));
    }
}

BiFunction Functional Interface

It represents a function that accepts two arguments and returns a result.

To define lambdas with two arguments, we have to use additional interfaces that contain the “Bi” keyword in their names: BiFunction, ToDoubleBiFunction, ToIntBiFunction, and ToLongBiFunction.
BiFunction interface from JDK 8: Let's look at the internal implementation of the BiFunction interface. BiFunction interface contains exactly one abstract method apply(T arg0, U arg1). Hence we can apply lambda expression to it.
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T arg0, U arg1);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> arg0) {
        Objects.requireNonNull(arg0);
        return (arg1, arg2) -> {
        return arg0.apply(this.apply(arg1, arg2));
     };
   }
}
Let's create an example to demonstrates the usage of the BiFunction functional interface:
public class BiFunctionExample {

    public static void main(String[] args) {
  
       BiFunction<Person, Person, Integer> biFunction = (p1,p2) -> {
           return p1.getAge() + p2.getAge();
       };
  
       int totalAge = biFunction.apply(new Person("Ramesh", 10), 
                               new Person("ram", 10));
       System.out.println(totalAge);
  
    }
}

BiConsumer Functional Interface

It represents an operation that accepts two input arguments and returns no result.
BiConsumer interface from JDK 8: Let's look at the internal implementation of BiConsumer interface. BiConsumer interface contains exactly one abstract method accept(T arg0, U arg1). Hence we can apply lambda expression to it.
@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T arg0, U arg1);

    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> arg0) {
        Objects.requireNonNull(arg0);
        return (arg1, arg2) -> {
             this.accept(arg1, arg2);
            arg0.accept(arg1, arg2);
        };
     }
}
Let's create an example to demonstrates the usage of the BiConsumer functional interface:

public class BiConsumersExample {

    public static void main(String[] args) {
  
        BiConsumer<Person, Person> biConsumer = (p1, p2) -> {
             System.out.println(" print first persion");
             System.out.println(p1.getName());
             System.out.println(" print second persion");
             System.out.println(p2.getName());
       };
  
       biConsumer.accept(new Person("Ramesh", 10), new Person("ram", 10));
    }
}

BiPredicate Functional Interface

This interface represents a predicate that takes two arguments.
This is how the interface is defined:
@FunctionalInterface 
public interface BiPredicate<T, U> {
     boolean test(T t, U u);
     // Default methods are defined also
}
The below example demonstrates the usage of the BiPredicate functional interface. Note the comments are self-descriptive.
package com.javaguides.java.functionalinterfaces;

import java.util.function.BiPredicate;

public class BiPredicateDemo {
    public static void main(String[] args) {

        // anonymous class implements BiPredicate interface
        BiPredicate < String, String > predicateObject = new BiPredicate < String, String > () {

            @Override
            public boolean test(String s1, String s2) {
                return s1.equals(s2);
            }
        };
        System.out.println(predicateObject.test("Ramesh", "Ramesh"));

        // Lambda expression implementation
        BiPredicate < String, String > biPredicate = (s1, s2) -> s1.equals(s2);
        System.out.println(biPredicate.test("ramesh", "ramesh"));
        System.out.println(biPredicate.test("java guides", "Java Guides"));
    }
}
Output:
true
true
false

Summary

  • A functional interface is an interface that has exactly one abstract method.
  • Since default methods have an implementation, they are not abstract so a functional interface can have any number of them.
  • If an interface declares an abstract method with the signature of one of the methods of java.lang.Object, it doesn't count toward the functional interface method count.
  • A functional interface is valid when it inherits a method that is equivalent but not identical to another.
  • An empty interface is not considered a functional interface.
  • A functional interface is valid even if the @FunctionalInterface annotation would be omitted.
  • Functional interfaces are the basis of lambda expressions.

Source Code on GitHub

The source code of this article is available on  GitHub Repository.

Comments