← Back to Advanced Java Course | Chapter 3: Functional Programming | Lesson 2 of 6

Java Functional Interfaces

A functional interface is a promise with exactly one job to do, so a lambda can fill it in. Predicate, Function, Consumer and Supplier are ready-made kinds, like standard tool shapes.
Syntax
java
@FunctionalInterface
interface InterfaceName {
    returnType methodName(parameters);  // exactly one abstract method
}

InterfaceName variable = (parameters) -> expression;

What is a Functional Interface?

A functional interface is any interface that contains exactly one abstract method, making it a valid target for a lambda expression or method reference. You can (and should) annotate one with @FunctionalInterface, which tells the compiler to verify the interface truly has just one abstract method and fail the build otherwise.

Example: What is a Functional Interface?

java
// Define the class Main
public class Main {
	@FunctionalInterface
	// Define the interface Operation
	interface Operation {
		int apply(int a, int b);
	}
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		Operation add = (a, b) -> a + b;
		// Print a line to the console
		System.out.println(add.apply(2, 3));
	}
}

The Predicate Interface

The Predicate interface is a built-in functional interface that accepts a single argument and returns a boolean, making it the standard shape for filtering conditions. It lives in the java.util.function package alongside Java's other general-purpose functional interfaces.

Example: The Predicate Interface

java
// Import java.util.function.Predicate so it can be used by its short name
import java.util.function.Predicate;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		Predicate<Integer> isEven = n -> n % 2 == 0;
		// Print a line to the console
		System.out.println(isEven.test(4));
		// Print a line to the console
		System.out.println(isEven.test(7));
	}
}

The Function Interface

The Function interface accepts a single argument of one type and returns a result of a potentially different type, representing a transformation from input to output. It's commonly used for data conversion or mapping operations, such as turning a String into an Integer.

Example: The Function Interface

java
// Import java.util.function.Function so it can be used by its short name
import java.util.function.Function;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		Function<String, Integer> toLength = s -> s.length();
		// Print a line to the console
		System.out.println(toLength.apply("hello"));
	}
}

Consumer and Supplier

The Consumer interface accepts a single argument and returns no result, representing an operation performed purely for its side effect, like printing a value. The Supplier interface is its mirror image: it accepts no arguments at all and simply produces a generated result on demand.

Example: Consumer and Supplier

java
// Import java.util.function.Consumer so it can be used by its short name
import java.util.function.Consumer;
// Import java.util.function.Supplier so it can be used by its short name
import java.util.function.Supplier;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		Consumer<String> printer = s -> System.out.println("Got: " + s);
		Supplier<String> generator = () -> "generated value";
		printer.accept(generator.get());
	}
}

BiFunctional Interfaces

Java also provides two-argument variants of these core interfaces for cases involving pairs of values: BiFunction accepts two arguments and returns a computed result, while BiConsumer accepts two arguments and performs a side effect without returning anything.

Example: BiFunctional Interfaces

java
// Import java.util.function.BiFunction so it can be used by its short name
import java.util.function.BiFunction;
// Import java.util.function.BiConsumer so it can be used by its short name
import java.util.function.BiConsumer;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
		BiConsumer<String, Integer> log = (name, age) -> System.out.println(name + " is " + age);
		// Print a line to the console
		System.out.println(multiply.apply(3, 4));
		log.accept("Sam", 30);
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Calling the wrong method name, such as predicate.apply(x); the method is test for Predicate, apply for Function, accept for Consumer and get for Supplier.
  2. Adding a second abstract method to an interface marked @FunctionalInterface, which causes a compile error because it must have exactly one.
  3. Expecting a Consumer to return a result, when it returns void; use Function if you need a value back.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.