Functional interfaces: the single abstract method rule¶
โ Java 17 ยท The One-Method Contract Behind Every Lambda ยท 1/3
Why a lambda needs a contract¶
This lambda has a parameter and a body, but no name and no type. On its own, Java can't tell what it's for. Its meaning comes from the type that receives it:
RatingRule has one method left to implement, check(). Java uses the lambda as its body: m is a Movie, and the result must be a boolean. This only works if there is exactly one blank to fill.
The SAM rule¶
An interface with exactly one abstract method is a functional interface. This is the Single Abstract Method rule, or SAM.
public interface RatingRule {
boolean check(Movie m); // the only abstract method
}
// option 1: a full class
public class TopRated implements RatingRule {
public boolean check(Movie m) { return m.rating() >= 8.0; }
}
// option 2: a lambda, same job, one line
RatingRule rule = m -> m.rating() >= 8.0;
A class can still implement a functional interface; a lambda is just the shortest way to do it.
Inherited methods count¶
An interface inherits its parent's abstract methods. Count them too:
public interface Playable { void play(Episode e); }
public interface Bingeable extends Playable { } // โ
inherits play(): 1 abstract
public interface Rewindable extends Playable { // โ play() + rewind(): 2 abstract
void rewind(int seconds);
}
Rewindable is a valid interface, but not a functional one: a lambda can't implement two methods.
Methods with a body don't count¶
Since Java 8 and 9, interfaces can contain methods with code. None of them count:
public interface Recommender {
List<Movie> suggest(User u); // abstract: counts
default int limit() { return 10; } // has a body
static Recommender none() { return u -> List.of(); }
private void log(String msg) { } // has a body
}
| Method kind | Counts toward the rule? |
|---|---|
| abstract (no body) | โ yes |
default |
no: it has a body |
static |
no |
private / private static |
no |
Recommender is functional: suggest() is its only abstract method. But an interface with only default, static or private methods has zero abstract methods, and zero isn't one: it's not functional.
Next: an annotation that checks the rule for you, and a loophole involving Object.