The Java "Rule" Everyone Gets Wrong About Functional Interfaces
Inspect the Java code snippet below. Can you guess whether it is a functional interface, or whether the compiler will report an error?
@FunctionalInterface
public interface IntSequence {
default boolean hasNext() {
return true;
}
int next();
static IntSequence digitsOf(int n) {
return new DigitSequence(n);
}
boolean equals(Object obj);
}
If you think it is not a functional interface, think again.
What Is a Functional Interface in Java?
According to the definition, any interface that contains exactly one abstract method is called a functional interface. It is also known as a Single Abstract
Method (SAM) interface and serves as the foundation for lambda expressions and functional programming in Java.
While this definition is accurate, it leaves out one important rule that many beginners overlook. The rule I’m talking about is:
Rule: Default methods and static methods do not count as abstract methods. Therefore,
hasNext()anddigitsOf()do not affect the interface’s functional-interface status.Methods that override members of
Object(such astoString(),equals(Object), andhashCode()) are also ignored when determining whether an interface is functional.
In the example above, next() is the only abstract method that counts. As a result, IntSequence is a valid functional interface.
Note: The Object-method exception applies only to methods inherited from
Object. Any other abstract method (for example,int length(String s)) is counted. If such a method were added,IntSequencewould no longer be a functional interface because it would then contain more than one abstract method.
When a lambda expression is assigned to IntSequence, it provides the implementation for next(), while the default implementation of hasNext() is inherited
automatically:
private static RandomGenerator generator = RandomGenerator.getDefault();
public static IntSequence randomInt(int low, int high) {
return () -> low + generator.nextInt(high - low + 1);
}
Notice that the lambda supplies only the implementation of next(). Java doesn’t require implementations for hasNext(), because it already has a default
implementation, nor for equals(Object), because methods inherited from Object are excluded from the functional-interface calculation.
Note: Explicitly declaring
equals(Object)in this interface is not normally necessary. It is included here solely to demonstrate that methods inherited fromObjectdo not affect functional-interface status.
The Takeaway
Many developers learn the simplified rule that a functional interface must have “exactly one abstract method” and stop there. While technically correct, the real rule is slightly more nuanced.
When determining whether an interface is functional:
- Default methods are ignored.
- Static methods are ignored.
- Methods that override members of
Objectare ignored. - Only abstract methods that are not inherited from
Objectare counted.
Once you understand this distinction, examples like IntSequence stop looking like exceptions and start looking exactly as the Java Language Specification intended.
The next time someone says, “That interface has two methods, so it can’t be functional,” you’ll know better.