Skip to main content

Command Palette

Search for a command to run...

Java Functional Interface

Updated
9 min read

What is a Functional interface?

The interface which contains single abstract method is called Functional interface.

Examples

  • Runnable interface - run()

  • Callable interface - call()

  • Comparable interface - compareTo()

  • Comparator interface - compare()

Functional interface could have default & static methods introduced in java 8

@FunctionalInterface
public interface ValidFunctionalInterf {
    void test();

    default void test1(){
        System.out.println("default method introduced in java 8");
    }
    
    public static void test2(){
        System.out.println("static method introduced in java 8");
    }
}

Identify valid/invalid Functional Interface

@FunctionalInterface
interface A{

    public void m1();

}

@FunctionalInterface
interface B extends A{

}

Above one is valid, as both the interface are marked as FunctionalInterface and has only one abstract method.

@FunctionalInterface
interface A{

    public void m1();

}

@FunctionalInterface
interface B extends A{
   public void m1();
}

valid - because both interface has only one abstract method

@FunctionalInterface
interface A{

    public void m1();

}

@FunctionalInterface
interface B extends A{
   public void m2();
}

Invalid - because interface B has two abstract methods, m1() is inherited from interface A in interface B.

@FunctionalInterface
interface A{

    public void m1();

}

interface B extends A{
   public void m2();
}

valid - interface A contains only one abstract method, and interface B is not marked as functional interface(it could have any number of abstract methods).

Built-in Functional Interfaces

The functional interfaces in the java.util.function package are introduced to enable functional-style programming in java by serving as the target types for lambda expressions and method references.

Interface Purpose Functional method
Predicate Evaluate a condition boolean test(T t)
Function<T,R> Transforms an input R apply(T t)
Consumer Performs an action on an input void accept(T t)
Supplier Provides a result without taking an input R get()

Predicate

Interface and method signature

public interface java.util.function.Predicate<T> {
  public abstract boolean test(T);
  public default java.util.function.Predicate<T> and(java.util.function.Predicate<? super T>);
  public default java.util.function.Predicate<T> negate();
  public default java.util.function.Predicate<T> or(java.util.function.Predicate<? super T>);
  public static <T> java.util.function.Predicate<T> isEqual(java.lang.Object);
  public static <T> java.util.function.Predicate<T> not(java.util.function.Predicate<? super T>);
}

Demo code

public class PredicateDemo {
    public static void main(String[] args) {
        Predicate<Integer> oddOrEven = num -> num % 2 == 0;
        System.out.println(oddOrEven.test(11)); //false
        System.out.println(oddOrEven.test(24)); //true
    }
}

Predicate Joining

consider below scenario

  • Predicate1 - returns the number which are even

  • Predicate2 - returns the number which are greater than 15

public class PredicateDemo {
    public static void main(String[] args) {
        Predicate<Integer> predicate1 = num -> num % 2 == 0;
        Predicate<Integer> predicate2 = num -> num > 15;
    }
}

Return the numbers which are even and greater than 15 -> p1.and(p2)

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.List;
import java.util.stream.Collectors;

public class Scenario1 {
    public static void main(String[] args) {
        Predicate<Integer> predicate1 = num -> num % 2 == 0;
        Predicate<Integer> predicate2 = num -> num > 15;

        List<Integer> list = Arrays.asList(1,12,16,18,19,22,23);

        List<Integer> result = list.stream()
                .filter(predicate1.and(predicate2))
                .collect(Collectors.toList());
        System.out.println(result);
    }
}

output

[16, 18, 22]

return the numbers which are even or greater than 10 -> p1.or(p2)

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.List;
import java.util.stream.Collectors;

public class Scenario2 {
    public static void main(String[] args) {
        Predicate<Integer> predicate1 = num -> num % 2 == 0;
        Predicate<Integer> predicate2 = num -> num > 15;

        List<Integer> list = Arrays.asList(1,12,16,18,19,22,23);

        List<Integer> result = list.stream()
                .filter(predicate1.or(predicate2))
                .collect(Collectors.toList());
        System.out.println(result);
    }
}

output

[12, 16, 18, 19, 22, 23]

return the number which is not even(odd number)-> p1.negate()

import java.util.Arrays;
import java.util.function.Predicate;
import java.util.List;
import java.util.stream.Collectors;

public class Scenario3 {
    public static void main(String[] args) {
        Predicate<Integer> predicate1 = num -> num % 2 == 0;

        List<Integer> list = Arrays.asList(1,12,16,18,19,22,23);

        List<Integer> result = list.stream()
                .filter(predicate1.negate())
                .collect(Collectors.toList());
        System.out.println(result);
    }
}

output

[1, 19, 23]

BiPredicate

Two input arguments, returns boolean

Interface and method signature

public interface java.util.function.BiPredicate<T, U> {
  public abstract boolean test(T, U);
  public default java.util.function.BiPredicate<T, U> and(java.util.function.BiPredicate<? super T, ? super U>);
  public default java.util.function.BiPredicate<T, U> negate();
  public default java.util.function.BiPredicate<T, U> or(java.util.function.BiPredicate<? super T, ? super U>);
}

demo code

import java.util.function.BiPredicate;
public class BiPredicateDemo {
    public static void main(String[] args) {
        BiPredicate<Integer,Integer> checkSumEven = (a,b)-> (a+b)%2==0;
        System.out.println(checkSumEven.test(10,20)); //true
        System.out.println(checkSumEven.test(11,2)); //false
    }
}

Note - and(), or() & negate() works same as Predicate in BiPredicate as demoed above in Predicate section

Primitive Predicate Types

Normal predicate type accept object only, it converts int to Integer(autoboxing) and back to int(autounboxing) while evaluating expressions. This impacts the performance, hence type based Predicates are available those we can use. Below are the types -

  • IntPredicate

  • LongPredicate

  • DoublePredicate

Demo code

import java.util.function.DoublePredicate;
import java.util.function.IntPredicate;
import java.util.function.LongPredicate;

public class PrimitivePredicateTypes {
    public static void main(String[] args) {
        IntPredicate p1 = num -> num > 10 ;
        System.out.println(p1.test(12));
        System.out.println(p1.test(9));

        LongPredicate p2 = num -> num % 2 ==0;
        System.out.println(p2.test(20L));
        System.out.println(p2.test(3L));

        DoublePredicate p3 = num -> num / 3 == 2;
        System.out.println(p3.test(6.0d));
        System.out.println(p3.test(1.0d));
    }
}

Function

Used to perform some operation and produce results.

Interface and method signature

public interface java.util.function.Function<T, R> {
  public abstract R apply(T);
  public default <V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T>);
  public default <V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V>);
  public static <T> java.util.function.Function<T, T> identity();
}

Example1

import java.util.function.Function;
public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,Integer> square = x -> x * x;
        System.out.println(square.apply(4)); //output - 16
    }
}

Example2

import java.util.function.Function;
public class FunctionDemo {
    public static void main(String[] args) {
        Function<String,Integer> len = String::length;
        /*
          Used method reference above, alternative is: s-> s.length()
         */
        System.out.println(len.apply("amol"));//output - 4
    }
}

Function Chaining

double the number then calculate the square of a number -> f1.andThen(f2)

import java.util.function.Function;
public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,Integer> f1 = x -> x * 2; // double
        Function<Integer,Integer> f2 = x -> x * x; //square
        Function<Integer,Integer> doubleThenSquare = f1.andThen(f2);
        System.out.println(doubleThenSquare.apply(5));//output - 100
    }
}

calculate the square of a number then double it -> f1.compose(f2)

import java.util.function.Function;
public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,Integer> f1 = x -> x * 2; // double
        Function<Integer,Integer> f2 = x -> x * x; //square
        Function<Integer,Integer> squareThenDouble = f1.compose(f2);
        System.out.println(squareThenDouble.apply(5));//output - 50
    }
}

BiFunction

Takes two input arguments and returns value of given type

Demo code

import java.util.function.BiFunction;
public class BiFunctionDemo {
    public static void main(String[] args) {
        BiFunction<Integer,Integer,Integer> squareOfSum = (a,b)-> {
          int c = a + b;
          return c * c;
        };
        System.out.println(squareOfSum.apply(1,1)); //4
    }
}

Primitive Function Type

Single argument with fixed input type and returns any type

  • IntFunction

  • LongFunction

  • DoubleFunction

Demo code

import java.util.function.IntFunction;

public class PrimitiveFunctionTypes {
    public static void main(String[] args) {
        IntFunction<String> cases = n -> {
            switch (n){
                case 1: return  "one";
                case 2: return "two";
                case 3: return "three";
                default: return "match not found";
            }
        };
        System.out.println(cases.apply(2));
        System.out.println(cases.apply(5));
    }
}

Note - Similar way we can use DoubleFunction & LongFunction which has fixed input type

single argument with fixed input and fixed output

  • IntToDoubleFunction - double applyAsDouble(int value)

  • DoubleToIntFunction - int applyAsInt(double value)

  • IntToLongFunction - long applyAsLong(int value)

  • DoubleToLongFunction - long applyAsLong(double value)

Demo Code

import java.util.function.IntToDoubleFunction;

public class IntToDoubleDemo {
    public static void main(String[] args) {
        IntToDoubleFunction ans = n -> n / 3.3333d;
        System.out.println(ans.applyAsDouble(10)); //3.000030000300003
    }
}

Single argument fixed return type

  • ToIntFunctionDemo

  • ToLongFunction

  • ToDoubleFunction

Demo code

import java.util.function.ToIntFunction;

public class ToIntFunctionDemo {
    public static void main(String[] args) {
        ToIntFunction<String> length = s -> s.length();
        System.out.println(length.applyAsInt("calculate length of string"));
    }
}

Two input args and fixed return type

  • ToIntBiFunction

  • ToDoubleBiFunction

  • ToLongBiFunction

Demo code

import java.util.function.ToIntBiFunction;
public class ToIntBiFunctionDemo {
    public static void main(String[] args) {
        ToIntBiFunction<Integer,Integer> ans = (a,b) -> a + b;
        System.out.println(ans.applyAsInt(10,30)); //40
    }
}

Consumer

consumer won't return anything, it just accept the data and consume it.

consumer interface and method signature

public interface java.util.function.Consumer<T> {
  public abstract void accept(T);
  public default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T>);
}

Demo code

import java.util.function.Consumer;
public class ConsumerDemo {
    public static void main(String[] args) {
        Consumer<String> printName = System.out::println;
        printName.accept("Amol");// output - Amol
    }
}

Consumer Chaining

import java.util.function.Consumer;
public class ConsumerDemo1 {
    public static void main(String[] args) {
        Consumer<String> message1 = s -> System.out.print("lets learn "+s);
        Consumer<String> message2 = s -> System.out.println(" In as simple way");
        Consumer<String> c = message1.andThen(message2);
        c.accept("consumer");//output - lets learn consumer In as simple way
    }
}

BiConsumer

Demo code - create arraylist using the BiConsumer concept

import java.util.List;
import java.util.Arrays;
import java.util.function.BiConsumer;

public class BiConsumerDemo {
    public static void main(String[] args) {
        BiConsumer<Integer,Integer> list = (a,b) -> {
            List<Integer> l = Arrays.asList(a,b);
            System.out.println(l);
        };
        list.accept(10,20); //[10, 20]
    }
}

Primitive types Consumer

  • IntConsumer

  • LongConsumer

  • DoubleConsumer

  • ObjIntConsumer

  • ObjLongConsumer

  • ObjDoubleConsumer

Supplier

Supplier won't take any input, it supplies only required object.

Interface and method signature

public interface java.util.function.Supplier<T> {
  public abstract T get();
}

Example1

import java.util.function.Supplier;
public class SupplierDemo {
    public static void main(String[] args) {
        Supplier<Date> d = ()-> new Date();
        System.out.println(d.get()); //output - Sat Apr 11 11:25:48 IST 2026
    }
}

Example2 - Supplier to get 6 digit random OTP

import java.util.function.Supplier;

public class SupplierDemo1 {
    public static void main(String[] args) {
        Supplier<String> randomOtp = ()-> {
            String otp = "";
            for (int i = 0; i < 6; i++) {
                otp += (int)(Math.random()*10);
            }
            return otp;
        };
        System.out.println(randomOtp.get());
        System.out.println(randomOtp.get());
        System.out.println(randomOtp.get());
        System.out.println(randomOtp.get());
    }
}

sample output - It is not necessary you will also get the same output

302627
225098
355752
986310