Java Functional Interfaces Cheat Sheet + Examples

Credits

Java Functional Interfaces

PREDICATE                           takes one (or two) argument(s) and returns a boolean (5 variants)
UNARY OPERATOR             result and the single argument types are the same (4 variants)
BINARY OPERATOR            result and both argument types are the same (4 variants)
FUNCTION                             result and one (or two) argument(s) types are different (17 variants)
SUPPLIER                               takes no arguments, returns a value ( 5 variants )
CONSUMER                           takes one (or two) arguments and returns no value (8 variants)

One Simple Example

        // Predicate
        Predicate<Integer> isPositive = num -> num > 0;
        System.out.println(isPositive.test(5)); // Output: true

        // Unary Operator
        UnaryOperator<Integer> square = num -> num * num;
        System.out.println(square.apply(5)); // Output: 25

        // Binary Operator
        BinaryOperator<Integer> sum = (num1, num2) -> num1 + num2;
        System.out.println(sum.apply(5, 3)); // Output: 8

        // Function
        Function<String, Integer> strLength = str -> str.length();
        System.out.println(strLength.apply("Hello")); // Output: 5

        // Supplier
        Supplier<Double> randomDouble = () -> Math.random();
        System.out.println(randomDouble.get()); // Output: Random double value

        // Consumer
        Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());
        printUpperCase.accept("hello"); // Output: HELLO

Supplier Interface

The Supplier Interface does not allow input, it returns a value based on a defined type. It is like a function without parameters, with a return type. Now, let’s see this behavior with examples.

public class SupplierInterface {
//Supplier function declarations.
Supplier textSupplier = () -> "Hello SW Test Academy!";
Supplier numberSupplier = () -> 1234;
Supplier randomSupplier = () -> Math.random();
Supplier randomSupplierMR = Math::random; //With Method Reference (MR)

@Test
public void supplierTest() {
    //Calling Supplier functions.
    System.out.println(textSupplier.get());
    System.out.println(numberSupplier.get());
    System.out.println(randomSupplier.get());
    System.out.println(randomSupplierMR.get());
}
}

Consumer Interface

The Consumer Interface takes an input, it does not return a value. It is like a function with a parameter, without a return type. BiConsumer Interface takes two inputs and does not return anything. That’s why it is called “Bi” Consumer. If we chain multiple consumers with andThen method, first the first consumer will be executed and the second one executed. It is working like left to right flow.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ConsumerInterface {
//Consumer function declarations.
Consumer upperCaseConsumer = (text) -> System.out.println(text.toUpperCase());
Consumer lowerCaseConsumer = (text) -> System.out.println(text.toLowerCase());
Consumer logOfTenConsumer = (number) -> System.out.println(Math.log10(number));

//BiConsumer takes two parameters and does not return anything!
BiConsumer<Integer, Integer> powConsumer = (base, power) -> System.out.println(Math.pow(base, power));

@BeforeEach
public void setup(TestInfo testInfo) {
    System.out.println("Test name: " + testInfo.getDisplayName());
}

@AfterEach
public void tearDown(){
    System.out.println();
}

@Order(1)
@Test
public void consumerTest() {
    //Calling Consumer functions.
    upperCaseConsumer.accept("Hello SW Test Academy!");
    lowerCaseConsumer.accept("Hello SW Test Academy!");
    logOfTenConsumer.accept(1000.00);
}

@Order(2)
@Test
public void biConsumerTest() {
    //Calling BiConsumer function.
    powConsumer.accept(3,2);
}

@Order(3)
@Test
public void consumerChainTest() {
    //Consumer chaining with andThen method.
    upperCaseConsumer
        .andThen(lowerCaseConsumer)
        .accept("Hello SW Test Academy!");
}
}

Function Interface

The Function Interface takes an input, it returns a defined type. It is like a function with a parameter, with a return type. The first declaration is inputthe second is the return type. The BiFunction Interface takes two inputs rather than one input. That’s the only difference between Function and BiFunction interfaces. Also, UnaryOperator interface takes and returns the same type. 

If we chain the functions with andThen method, the execution order will be like the left to right flow. First, the first function will be run, then the others will be run. If we want to run these functions right to left we can use compose method rather than the andThen method. 

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class FunctionInterface {
//FunctionInterface function declarations. (Input Type, Return Type)
Function toUpperCase = (text) -> text.toUpperCase();
Function toLowerCase = (text) -> text.toLowerCase();
Function log10 = (number) -> Math.log10(number);

//Method Reference Declarations (Input Type, Return Type)
Function<String, String>  toUpperCaseMR = String::toUpperCase;
Function<String, String>  toLowerCaseMR = String::toLowerCase;
Function<Integer, Double> log10MR       = Math::log10;

//BiFunction Example (Input Type, Input Type, Return Type)
BiFunction<Integer, Integer, Integer> powerOf = (base, power) -> (int) Math.pow(base, power);

//UnaryOperator Example (Input and Return type are same.)
UnaryOperator<String> appendText = (text) -> "I am appending: " + text;

@BeforeEach
public void setup(TestInfo testInfo) {
    System.out.println("Test name: " + testInfo.getDisplayName());
}

@AfterEach
public void tearDown(){
    System.out.println();
}

@Order(1)
@Test
public void functionTest() {
    //Calling functions.
    String upperCaseResult = toUpperCase.apply("hello sw test academy!");
    Double log10Result = log10.apply(10000);

    System.out.println(upperCaseResult);
    System.out.println(log10Result);
}

@Order(2)
@Test
public void functionChainWithAndThen() {
    //Function chaining. First do the first function then do the second one.
    String chainResult1 = toUpperCase.andThen(toLowerCase).apply("heLLo sW teSt ACadEmy!");
    String chainResult2 = toLowerCase.andThen(toUpperCase).apply("heLLo sW teSt ACadEmy!");

    System.out.println(chainResult1);
    System.out.println(chainResult2);
}

@Order(3)
@Test
public void functionChainWithCompose() {
    //Function chaining. First do the second function then do the first one. Vise versa of andThen.
    String chainResult1 = toUpperCase.compose(toLowerCase).apply("heLLo sW teSt ACadEmy!");
    String chainResult2 = toLowerCase.compose(toUpperCase).apply("heLLo sW teSt ACadEmy!");

    System.out.println(chainResult1);
    System.out.println(chainResult2);
}

@Order(4)
@Test
public void biFunctionTest() {
    //Calling functions.
    int result = powerOf.apply(3, 2);
    System.out.println("Power of 3 over 2 is: " + result);
}

@Order(5)
@Test
public void unaryOperatorTest(){
    //Calling UnaryOperator
    System.out.println(appendText.apply("Hello SW Test Academy!"));
}
}

Predicate Interface

The predicate takes an input, it returns a boolean value as true or false. It is like a function with a parameter, with the boolean return type. BiPredicate Interface takes two inputs and returns a boolean value.

Negate method does the inversion for the value.

And method is working as logical AND operation.

Or method is working as a logical OR operation.

Let’s see all of these with examples.

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PredicateInterface {
//Predicate function declaration.
String sampleText = "Hello SW Test Academy";
Predicate containsPredicate = (text) -> sampleText.contains(text);

//BiPredicate function declaration.
BiPredicate<String, String> containsBiPredicate   = (text, pattern) -> text.contains(pattern);
BiPredicate<String, String> containsBiPredicateMR = String::contains; //Method reference version.

@BeforeEach
public void setup(TestInfo testInfo) {
    System.out.println("Test name: " + testInfo.getDisplayName());
}

@AfterEach
public void tearDown(){
    System.out.println();
}

@Order(1)
@Test
public void predicateTest() {
    //Calling Predicate functions.
    boolean result = containsPredicate.test("SW");
    boolean resultOfNegate = containsPredicate.negate().test("SW"); //negate is inverse operation like "does not contain".
    boolean andResult = containsBiPredicate.and(containsBiPredicate.negate()).test("SW", "SW"); //Logical AND operation.
    boolean orResult = containsBiPredicate.or(containsBiPredicate.negate()).test("SW", "SW"); //Logical OR operation.

    System.out.println(result);
    System.out.println(resultOfNegate);
    System.out.println(andResult);
    System.out.println(orResult);
}

@Order(2)
@Test
public void predicateListTest() {
    List<Predicate<String>> predicateList = new ArrayList<>();
    predicateList.add(containsPredicate);
    predicateList.add(containsPredicate.negate());

    predicateList
        .forEach(predicate -> System.out.println(predicate.test("SW")));
}

@Order(3)
@Test
public void biPredicateTest() {
    //Calling BiPredicate functions.
    boolean result = containsBiPredicate.test("Hello SW Test Academy", "SW");
    System.out.println(result);
}
}

Runnable Interface

The Runnable does not allow input, it does not return value. It is like a function without parameters, without return type. Let’s make it tangible with examples.

public class RunnableInterface {
//Runnable function declarations.
Runnable runFunction = () -> System.out.println("I am running!");

@Test
public void runnableTest() {
    //Calling Runnable functions.
    runFunction.run();
}

//Running Asynchronously
public static void main(String[] args) {
    Runnable runFunction = () -> System.out.println("I am running!");

    Runnable runWithDelay = () -> {
        Uninterruptibles.sleepUninterruptibly(3000, TimeUnit.MILLISECONDS);
        System.out.println("I am running Asynchronously!");
    };

    new Thread(runWithDelay).start();

    runFunction.run();
}
}

Callable Interface

Callable does not allow input, it returns a value. It is like a function without parameters, with the return type. An example is as follows.

public class CallableInterface {
//Callable function declarations.
Callable callFunction = () -> Math.random() * 100;
Supplier supplierFunction = () -> Math.random() * 100;

@SneakyThrows
@Test
public void callableTest() {
    //Calling functions.
    int callResult = callFunction.call().intValue();
    int getResult = supplierFunction.get().intValue();

    System.out.println(callResult);
    System.out.println(getResult);
}
}
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments