Coding Functional Interfaces
OCP Exam Topic — Define and implement functional interfaces. Covered in Chapter 8 of OCP Java SE 21 Developer Study Guide (Selikoff & Boyarsky).
What Is a Functional Interface?
A functional interface is any interface that contains exactly one abstract method. That single abstract method (SAM) is the one a lambda or method reference implements. The number of default or static methods does not matter — only the abstract method count.
java@FunctionalInterface
interface Greeter {
void greet(String name); // single abstract method
}Usage:
javaGreeter g = name -> System.out.println("Hello, " + name);
g.greet("Alice"); // Hello, AliceThe @FunctionalInterface Annotation
Adding @FunctionalInterface to an interface tells the compiler to enforce the SAM rule. If the interface has zero or two or more abstract methods, the code will not compile.
java@FunctionalInterface
interface Valid {
int compute(int x); // OK — one abstract method
}
@FunctionalInterface
interface Invalid { // compile error — two abstract methods
int add(int a, int b);
int subtract(int a, int b);
}The annotation is optional — any interface with exactly one abstract method is technically functional — but it is a best practice and protects you from accidentally adding a second abstract method later.
Default and Static Methods Are Allowed
A functional interface may contain any number of default and static methods. These do not count toward the SAM requirement.
java@FunctionalInterface
interface Validator<T> {
boolean validate(T value); // SAM
default Validator<T> and(Validator<T> other) {
return value -> this.validate(value) && other.validate(value);
}
static <T> Validator<T> alwaysTrue() {
return value -> true;
}
}javaValidator<String> notEmpty = s -> !s.isEmpty();
Validator<String> notBlank = s -> !s.isBlank();
Validator<String> combined = notEmpty.and(notBlank);
System.out.println(combined.validate("hi")); // true
System.out.println(combined.validate(" ")); // falseOverriding Object Methods
Abstract methods that override Object public instance methods (such as toString, equals, hashCode) do not count as the SAM for functional interface purposes, because every implementation inherits them from Object.
java@FunctionalInterface
interface Converter<T, R> {
R convert(T input); // SAM
@Override
String toString(); // does NOT count — overrides Object.toString()
}Generic Functional Interfaces
Functional interfaces can be generic, letting a single definition cover multiple types.
java@FunctionalInterface
interface Mapper<T, R> {
R map(T input);
}javaMapper<String, Integer> length = s -> s.length();
Mapper<Integer, String> toHex = n -> Integer.toHexString(n);
System.out.println(length.map("Java")); // 4
System.out.println(toHex.map(255)); // ffThe type parameters are resolved at the point of assignment from the lambda's target type.
Common Mistakes on the Exam
| Scenario | Compiles? |
|---|---|
Interface has one abstract method, no @FunctionalInterface |
Yes — still functional |
@FunctionalInterface on interface with two abstract methods |
No |
@FunctionalInterface on interface with one abstract + one default |
Yes |
@FunctionalInterface on interface with one abstract + one static |
Yes |
@FunctionalInterface on interface that extends another with its own SAM |
Depends — total abstract methods must equal 1 |
javainterface Base {
void doWork(); // abstract
}
@FunctionalInterface
interface Derived extends Base {
// inherits doWork() — still only 1 abstract method, valid
}
@FunctionalInterface // compile error — adds a second abstract method
interface BadDerived extends Base {
void doMore();
}Defining Your Own vs. Using Built-in Interfaces
Java provides a rich set of built-in functional interfaces in java.util.function (covered in lesson 8-4). Before defining a custom functional interface, check whether one of the built-in types already fits your method signature. Custom interfaces are appropriate when:
- You need a more descriptive name for domain clarity.
- The built-in generics cannot express a checked exception that your method must declare.
- You need a primitive-specialised variant not covered by the built-in set.
Key Points to Remember
- A functional interface has exactly one abstract method (the SAM).
@FunctionalInterfaceis optional but strongly recommended — it turns SAM violations into compile errors.defaultandstaticmethods do not count against the single-abstract-method rule.- Abstract methods that merely re-declare
Objectpublic methods also do not count. - Generic functional interfaces resolve their type parameters from the lambda's assignment context.
- Always prefer the built-in
java.util.functioninterfaces before creating a new one.