Q17 of 40 · Core Java
What are functional interfaces? Provide 3 examples from the standard library.
Short answer
Short answer: A functional interface has exactly one abstract method and can be the target of a lambda or method reference. The @FunctionalInterface annotation is optional but enforces the contract at compile time. Key standard examples: Predicate<T> (test), Function<T,R> (apply), Consumer<T> (accept), Supplier<T> (get), Runnable (run).
Detail
A functional interface is any interface with a single abstract method (SAM). It can have any number of default or static methods. The @FunctionalInterface annotation is optional — Java will treat any SAM interface as functional — but adding it causes the compiler to verify that the interface still has exactly one abstract method, guarding against accidental addition of a second one.
Lambdas are syntactic sugar for anonymous implementations of a functional interface. When you write list.removeIf(s -> s.isEmpty()), the lambda s -> s.isEmpty() is treated as an instance of Predicate<String>.
Three key standard-library functional interfaces (from java.util.function):
Predicate<T>— takes a T, returnsboolean. Abstract method:test(T t). Used inStream.filter(),List.removeIf(),Optional.filter(). Useful for reusable filter conditions in test data selection.Function<T, R>— takes a T, returns an R. Abstract method:apply(T t). Used inStream.map(). Compose withandThen()andcompose()for transformation pipelines.Consumer<T>— takes a T, returns nothing. Abstract method:accept(T t). Used inStream.forEach(),Optional.ifPresent(). Appropriate for side effects: logging, assertions, sending data.
Other commonly cited: Supplier<T> (takes nothing, returns T — used in Optional.orElseGet(), lazy initialisation), BiFunction<T,U,R>, UnaryOperator<T>.
// EXAMPLE
FunctionalInterfaces.java
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
// Predicate<T> — returns boolean, used for filtering
Predicate<String> isSmoke = tag -> "smoke".equals(tag);
Predicate<String> isShort = tag -> tag.length() <= 4;
Predicate<String> isSmokeOrShort = isSmoke.or(isShort); // composable
List<String> smokeTags = tags.stream()
.filter(isSmoke)
.toList();
// Function<T,R> — transforms T to R
Function<String, Integer> wordCount = s -> s.split("\s+").length;
Function<String, String> trim = String::trim; // method reference
Function<String, Integer> trimThenCount = trim.andThen(wordCount);
// Consumer<T> — side effect, no return value
Consumer<TestResult> logger = r ->
System.out.printf("[%s] %s%n", r.passed() ? "PASS" : "FAIL", r.id());
results.forEach(logger);
result.ifPresent(logger); // Optional.ifPresent() takes a Consumer
// Supplier<T> — produces a value, takes nothing
Supplier<List<String>> emptyTagList = ArrayList::new;
Map<String, List<String>> grouped = new HashMap<>();
grouped.computeIfAbsent("smoke", k -> emptyTagList.get());
// Custom functional interface
@FunctionalInterface
interface TestDataBuilder<T> {
T build(long seed);
}
TestDataBuilder<User> userFactory = seed -> new User("user" + seed);