Java 8's Consumer, Predicate and Supplier Functional Interfaces

Java has introduced functional programming support in Java release version 8. This release added several key changes into the language like Lambda Expressions, Functional Interfaces, Streams, etc. There are few functional interfaces namely Consumer, Supplier, Predicate are most crucial. In this article, we will talk about these interfaces.

What is Functional Interface?

Functional interface is nothing but the interface which contains only one abstract method but can have multiple default and static methods. To put it in simple words Any interface with a SAM(Single Abstract Method) is a functional interface.

Functional interfaces are significant because they are the only types that can serve as the context for a lambda expression or method reference in Java 8.

Few Default Functional Interfaces in Java:

Runnable -> run()
Callable -> call()
Comparable -> compareTo(T o)
Comparator -> compare(T o1, T o2)
Consumer -> accept(T t)
Predicate -> test(T t)
Supplier -> get()

@FunctionalInterface annotation

When you annotate an interface with this, It will make sure you don't have more than one abstract method at compile time. This is optional , you may ignore as a programmer.

If the programmer specifies annotation then interface can contain only one abstract method. If the programmer specifies more than one abstract method, then compiler show error message “Invalid ‘@FunctionalInterface’ annotation”.

It's always best practice to use this annotation when you want an interface needs to serve as functional interface.

Consumer

A Consumer is an in-built functional interface introduced in Java8. Consumer can be used in all contexts where an Object needs to be consumed i.e, taken as input and some operation is to be performed on the Object without returning any result. (OR) A Consumer is a functional interface that accepts a single input and returns no output. As the name suggests the implementation of this interface consumes the input supplied to it.

Real Time Example: Lets assume you have a list of items and you want to print the name of items in capital letters. you must have to perform some operation on the name of each Item, right? Here you can use Consumer which accepts Item object as input and prints the item names in capital letters.

Consumer interface has two methods:

void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after);

Demonstration

Item.java

public class Item {

    private String itemName;
    private int price;

    public Item() {

    }

    public String getItemName() {
        return itemName;
    }
    public void setItemName(String itemName) {
        this.itemName = itemName;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }

    public Item(String itemName, int price) {
        super();
        this.itemName = itemName;
        this.price = price;
    }

}

ConsumerFIforDemo.java

public class ConsumerFIforDemo {

    public static void main(String[] args) {

        List<Item> items = new ArrayList<>();
        items.add(new Item("chicken", 180));
        items.add(new Item("pizza",150));
        items.add(new Item("burger",70));
        items.add(new Item("chips", 50));

        Consumer<Item> consumer = (c) -> 
        System.out.println(c.getItemName().toUpperCase());

        System.out.println("Without lambda expression\n");
        items.forEach(consumer);

        //You can write in single like below, forEach accepts consumer as an argument.
        //we can leverage the lambda expressions to implement the consumer abstract method like below.

        System.out.println("\nWith lambda expression\n");
        items.forEach(item -> System.out.println(item.getItemName().toUpperCase()));
    }
}

Output:

Without lambda expression

CHICKEN
PIZZA
BURGER
CHIPS

With lambda expression

CHICKEN
PIZZA
BURGER
CHIPS

Predicate

This Functional Interface is used for conditional check. It represents a boolean-valued-function of an argument. This is mainly used to filter data from a Java Stream.

We can use this whenever we want to check something and return true or false based on condition.

A predicate has a test() method which accepts an argument and returns a boolean value.

It has an abstract method

 boolean test(T t);

Predicate also provides a few default and static method for composition and other purposes:

default Predicate<T> and(Predicate<? super T> other);
default Predicate<T> or(Predicate<? super T> other);
static <T> Predicate<T> isEquals(Object targetRef);
default Predicate<T> negate();

Real Time Example Lets assume you have list of Items and you want to filter these items based on a condition like, print the items whose price is greater than 500.

Demonstration

Item.java

public class Item {

    private String itemName;
    private int price;

    public Item() {

    }

    public String getItemName() {
        return itemName;
    }
    public void setItemName(String itemName) {
        this.itemName = itemName;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }

    public Item(String itemName, int price) {
        super();
        this.itemName = itemName;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Item [itemName=" + itemName + ", price=" + price + "]";
    }

}

PredicateFIforDemo.java

public class PredicateFIforDemo {

    public static void main(String[] args) {

        List<Item> items = new ArrayList<>();
        items.add(new Item("chicken", 650));
        items.add(new Item("pizza",250));
        items.add(new Item("burger",770));
        items.add(new Item("chips", 530));
        items.add(new Item("French Fries",570));
        items.add(new Item("Shawarma", 140));

        //Returns true if item price is > 500
        Predicate<Item> pFI = (item) -> item.getPrice() > 500;
        items.stream().filter(pFI).forEach(item -> System.out.println(item));    
    }
}

Output

Item [itemName=chicken, price=650]
Item [itemName=burger, price=770]
Item [itemName=chips, price=530]
Item [itemName=French Fries, price=570]

Supplier

This Functional Interface is used in all contexts where there is no input but an output is expected.

Real Time Example Lets assume we have list of items and we are filtering these based on a condition but after filtering we are not getting result so that we want to return a dummy result.

Please refer above Item.java. Below code demonstrates the Supplier Functional Interface.

SupplierFIforDemo.java

public class SupplierFIforDemo {

    public static void main(String[] args) {


        List<Item> items = new ArrayList<>();
        items.add(new Item("chicken", 650));
        items.add(new Item("pizza",250));
        items.add(new Item("burger",770));
        items.add(new Item("chips", 530));
        items.add(new Item("French Fries",570));
        items.add(new Item("Shawarma", 140));

        //Returning an Item with no valid data
        Supplier<Item> supplier = () -> new Item("Dummy", 0);    

        System.out.println(items.stream().filter(item -> 
        item.getPrice()>1000).findAny().orElseGet(supplier));

    }
}

Output

Item [itemName=Dummy, price=0]

Thanks for reading :)

You can follow me at LinkedIn