Functional Interfaces in Java

Functional Interface(FI) refers to a special kind of interface, it's an interface with only one abstract method, that is, FI can have many other implemented methods, but only one abstract method.

package com.sosv.functional.generic;

public interface GenericInterface {

  String numberToText(String lang, Long number);

  default long countDigits(Long number) {
    return (long)(Math.log10(number) + 1);
  }
}

We can annotate with @FunctionalInterface an interface to validate that interface has only one abstract method.

@FunctionalInterface
public interface GenericInterface {

  String numberToText(String lang, Long number);
  default long countDigits(Long number) {
    return (long)(Math.log10(number) + 1);
  }

}

Before FI, if you want to create an instance of an interface, you can create a new class that implements the interface or create an anonymous class.

For example:

//Create new Class
public class GenericClass implements GenericInterface {
    @Override
    public String numberToText(String lang, Long number) {
        return "";
    }
}

var generic = new GenericClass();

//Annonymous class
var generic = new GenericInterface() {
    @Override
    public String numberToText(String lang, Long number) {
        return "";
    }
};

Lambda Expressions(LE)

Since java SE 8, In addition to FI, we have lambda expressions. For me, LE is an implementation of a FI. We can reduce the above verbose code into cleaner code, easy to read.

GenericInterface generic = (String lang, Long number) -> "";

The basic notation for a lambda expression is:

(<parameters>) -><arrow operator> <body>

parameters: It can be 0, 1, or N parameters, you can specify the type of all parameters or neither parameter. Ex: (String lang, Long number) or (lang, number)

arrow operator: It's a "-", followed by ">". Ex: ->

body: It can have {} or not. depends on the implementation of the lambda expression.

If body doesn't have {}, return keyword is implicit;

{} it's mandatory when the abstract method doesn't have a return type, that is, void.

Ex: "" or {return "";}

Kinds of FI

We have basically 4 types of FI, and all of these FI are in java.util.function package.

FIInputReturn
Predicate(T param)Boolean
Function(T param)(R response)
Supplierno param(R response)
Consumer(T param)no response

T, R: Generic.

//Predicate
Predicate<String> hasNumber = (text) ->  text.matches(".*\\d+.*");
//Function
Function<Integer, Integer> square = (num1) -> num1 * num1;
//Supplier
Supplier<Double> random = () -> Math.random();
//Consumer
Consumer<String> logger = (text) -> System.out.println(text);

Operations of FI

//Predicate
Predicate<String> hasNumber = (text) ->  text.matches(".*\\d+.*");
Predicate<String> isLargeText = (text) ->  text.length() > 500;
hasNumber.negate() // or (text) ->  !text.matches(".*\\d+.*");
hasNumber.and(isLargeText) // or (text) -> text.matches(".*\\d+.*") && text.length() > 500
hasNumber.or(isLargeText)  // or (text) -> text.matches(".*\\d+.*") || text.length() > 500
hasNumber.test("Hello") //To get result of predicate


//Function
Function<Integer, Integer> square = (num1) -> num1 * num1;
Function<Integer, Integer> triple = (num1) -> num1 * 3;
square.andThen(triple) // or num*num*3
triple.andThen(square) // or (3*num)*(3*num)
triple.apply(2) // return 6

//Supplier
Supplier<Double> random = () -> Math.random();
random.get() // to get value

//Consumer
Consumer<String> logger = (text) -> System.out.println(text);
Consumer<String> greetings = (name) -> System.out.println("Hello " + name);
logger.andThen(greetings) // or print text and "Hello" + text
logger.accept("Hashnode") // to execute consumer

I hope you enjoy this summary of FI and LE.

1
Subscribe to my newsletter

Read articles from Sleyter Sandoval directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sleyter Sandoval
Sleyter Sandoval

I'm peruvian. I'm OCP Java 11 certified.