skip to content

What is a functional interface in Java, and what does the @FunctionalInterface annotation do?

level: juniorimportance: must knowfreq 78%

answer

  1. SAM = Single Abstract Method
  2. Lambda needs a functional-interface target type
  3. default/static/private/Object methods don't count
  4. @FunctionalInterface is optional but enforces the rule
  5. java.util.function: Function/Predicate/Supplier/Consumer

basics

~20 s

A functional interface is an interface with exactly one abstract method, so it can be implemented by a lambda. @FunctionalInterface is an optional annotation that makes the compiler check that the interface really has just one abstract method.

solid answer

~40 s

A functional interface is an interface with exactly one abstract method (a SAM type). Because there is only one method to implement, the compiler can treat a lambda expression or method reference as an instance of that interface. Examples in the JDK include Runnable, Comparator, Callable, and the java.util.function types like Function, Predicate, and Supplier. The optional @FunctionalInterface annotation documents the intent and asks the compiler to fail the build if the interface ever stops having exactly one abstract method. Importantly, default methods, static methods, private methods, and re-declarations of public methods from java.lang.Object (like equals or toString) do not count toward the single-abstract-method limit, so an interface can have many of those and still be functional.

go deeper

for a junior

Can define a functional interface as 'one abstract method' and knows a lambda implements it; recognizes Runnable and Comparator as examples.

for a middle

Explains precisely which methods don't count (default/static/private/Object), names the java.util.function families, and knows @FunctionalInterface is optional but enforcing.

for a senior

Reasons about lambda target typing and overload resolution, designs custom functional interfaces sparingly (preferring java.util.function), and understands why the annotation guards API stability.

for a principal

Weighs API evolution risk: adding a second abstract method is a breaking change; uses @FunctionalInterface deliberately on published SAM types and chooses standard vs custom interfaces for library ergonomics and binary compatibility.

## What problem this solves Before Java 8, to pass a small piece of behavior to a method (for example, telling `Collections.sort` how to order elements), you had to write a whole anonymous class. That was verbose. Java 8 introduced **lambda expressions** — a compact syntax for an anonymous function — and **method references**. But Java is statically typed: every value must have a type. A lambda has no class of its own, so the language needs to know *what interface* the lambda is implementing. That target type is a **functional interface**. ## Definition An **interface** is a contract: a named set of methods a class promises to provide. An **abstract method** is a method with no body — just a signature — that an implementer must fill in. A **functional interface** is simply an interface that has **exactly one abstract method**. Because there is exactly one method to supply, the compiler can take a lambda (a bare method body) and know it belongs to that single method. The jargon term for this is a **SAM type** (Single Abstract Method). ```java interface Greeter { // functional: one abstract method String greet(String name); } Greeter g = name -> "Hi " + name; // lambda implements greet ``` ## What counts as "the one abstract method" The rule is more precise than "one method total." These do **not** count against the single-abstract-method requirement: - **default methods** — methods in an interface that already have a body (introduced in Java 8). - **static methods** — methods called on the interface itself, not on an instance. - **private methods** — internal helpers (Java 9+). - **public methods that override `java.lang.Object`** — for example, an interface may re-declare `boolean equals(Object)` or `String toString()`. Since every object already inherits these from `Object`, they are not a new abstract method to implement. So an interface can have many default/static methods and still be a valid functional interface, as long as **exactly one** truly-abstract, non-Object method remains. `Comparator<T>` is the classic example: its single abstract method is `compare(T, T)`, yet it has many default and static helpers like `reversed()` and `thenComparing()`. ## @FunctionalInterface `@FunctionalInterface` is an **annotation** — metadata you place above the interface. It is **optional**: an interface is functional based on its shape, not on the annotation. Its job is twofold: 1. **Documentation** — it signals to readers "this is meant to be used as a lambda target." 2. **Compile-time enforcement** — if you add a second abstract method later, the build fails with an error. Without the annotation, adding a second abstract method silently and quietly breaks every lambda that targeted it, but only at the call sites, with a confusing message. With the annotation, the error points right at the interface. ```java @FunctionalInterface interface Transformer { String apply(String s); // adding 'int count();' here would now fail to compile } ``` ## The JDK's standard functional interfaces The package `java.util.function` provides ready-made functional interfaces so you rarely need to invent your own: `Function<T,R>` (takes a T, returns an R), `Predicate<T>` (takes a T, returns boolean), `Supplier<T>` (takes nothing, returns a T), `Consumer<T>` (takes a T, returns nothing), `BiFunction`, `UnaryOperator`, and primitive-specialized variants like `IntPredicate`. Older single-method interfaces — `Runnable`, `Callable`, `Comparator` — are also functional and work with lambdas. ## Why it matters Functional interfaces are the bridge that makes lambdas, method references, and the Streams API possible. Every `stream.map(x -> ...)`, every `list.forEach(System.out::println)`, every `Comparator.comparing(...)` relies on a functional interface as the target type.

  • Can a functional interface declare equals(Object) as an abstract method and still be functional?
    Yes. Methods that override a public method of java.lang.Object (equals, hashCode, toString) do not count as the single abstract method, so the interface can still have one genuine abstract method and remain functional.
  • Is @FunctionalInterface required to use a lambda?
    No. Any interface with exactly one abstract method can be a lambda target. The annotation only adds documentation and a compile-time check; it does not enable the feature.

saying these in an interview costs you the question

  • Saying a functional interface can have only one method total (it can have many default/static methods)
  • Believing @FunctionalInterface is required for lambdas to work
  • Forgetting that re-declared Object methods (equals/toString) don't count toward the SAM limit
  • Confusing 'functional interface' with 'marker interface' (marker has zero methods)

context