What is a functional interface in Java, and how does the compiler know a lambda can be assigned to it?
A functional interface is an interface with exactly one abstract method. Everything else in it may be default methods, static methods or constants. The single abstract method is called the single abstract method, or SAM, and it is what a lambda implements.
The compiler works it out by counting the abstract methods. If there is exactly one, the interface is a functional interface and a lambda is a valid implementation of it. If there are zero — an interface with only default methods — it is not a functional interface, and no lambda can be assigned to it. If there are two or more, it is not one either, and the compiler rejects the lambda assignment.
The @FunctionalInterface annotation is optional in the language but worth always adding. It is a compile-time check: if somebody later adds a second abstract method to the interface, every lambda that implemented it stops compiling, and the annotation turns that into a clear error at the interface itself rather than a confusing cascade of errors at every use site.
Two consequences of the SAM rule are worth knowing. Because the method is unique, the compiler can treat the interface as a function type and you can pass implementations around without declaring a separate class. And because the interface has a single method, the inherited methods from Object — equals, hashCode and toString — do not count towards the total. An interface with one method plus those three is still a functional interface.
The practical note: a functional interface describes one operation, so name it after that operation rather than after the implementation. Naming it Consumer or Transformer rather than ServiceProcessor makes it obvious that it is meant to be implemented by a lambda.