CodeBlanks Logo Icon CodeBlanks

Summary - Functional interface

Combine Function, Predicate and Consumer to write a stream processor.

Summary - Functional interface

In this group you met the four core functional interfaces and the @FunctionalInterface annotation. Function transforms, Predicate returns a condition, Consumer consumes, Supplier produces. All have a single abstract method and can be written as lambdas.

Combining these interfaces yields short, readable code. For example you pass a Predicate to filter, a Function to map and a Consumer to forEach. This is the standard way to express stream operations in a single line.

Practice Function + Predicate + Consumer chain

Define a Function<String, Integer> len = String::length. Define a Predicate<Integer> big = n -> n > 4. Loop over List.of("pen", "desk", "notebook"); when big.test(len.apply(w)) is true, print w with System.out.println.

Starter code

Main.java
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

// Define length Function and condition Predicate
// Loop and print based on the condition

Sample solution

Main.java
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

public class ChainFunctional {
    public static void main(String[] args) {
        Function<String, Integer> len = String::length;
        Predicate<Integer> big = n -> n > 4;
        for (String w : List.of("pen", "desk", "notebook")) {
            if (big.test(len.apply(w))) {
                System.out.println(w);
            }
        }
    }
}

Expected output

desk
notebook

When to define a custom interface?

In most cases the interfaces in java.util.function are enough. You define a custom interface only when the function has a specific meaning or when the primitive variants do not fit.

For example a User -> boolean condition is usually carried as Predicate<User>. But if writing Predicate<User> everywhere hurts readability, defining @FunctionalInterface interface UserRule may be cleaner. The choice depends on the long-term readability of the code.

Practice Define and use a custom StringOp interface

Define a StringOp interface with @FunctionalInterface; it should have a String transform(String input) method. In main, assign the String::toUpperCase method reference to a StringOp variable. Print the result of transform("hello") with System.out.println.

Starter code

Main.java
// Define the StringOp interface (one abstract method)
// Its transform method takes and returns a String
// In main, use upper, print the result

Sample solution

Main.java
@FunctionalInterface
interface StringOp {
    String transform(String input);
}

public class CustomStringOp {
    public static void main(String[] args) {
        StringOp upper = String::toUpperCase;
        System.out.println(upper.transform("hello"));
    }
}

Expected output

HELLO

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Which interface takes a value and returns void?

  • Function<T,R>
  • Consumer<T> Correct answer
  • Predicate<T>
  • Supplier<T>

Explanation: Consumer<T> consumes the value and returns void.

2 Match each interface with its behavior

Term Meaning
Function<T,R> Transforms
Predicate<T> Returns a condition
Consumer<T> Consumes
Supplier<T> Produces

All lessons in Advanced