What does the @FunctionalInterface annotation do, and what happens if you apply it to an interface with two abstract methods?
answer
- Only effect: compiler counts abstract methods, must equal one
- Two abstract methods → compile error at the interface
- Optional, like @Override
- Freezes the contract so adding a method can't silently break lambdas
basics
~20 s@FunctionalInterface tells the compiler to check that the interface has exactly one abstract method. If you put it on an interface with two abstract methods, the code fails to compile. It is optional and only adds this check.
solid answer
~50 s@FunctionalInterface is a marker annotation whose only effect is a compile-time check: the compiler verifies the annotated interface has exactly one abstract method. If the interface has zero abstract methods, or two or more, compilation fails with an error. The annotation is purely optional — any interface with a single abstract method can be used with lambdas whether or not it is annotated. Its value is twofold: it documents intent (this type is designed for lambda/method-reference use) and it freezes that contract so a future change that adds a second abstract method is caught immediately at the interface, rather than silently breaking every lambda call site downstream. It is the same idea as @Override: optional, but it converts a latent assumption into an enforced invariant. The JDK annotates Runnable, Comparator, Callable, and the java.util.function types with it.
go deeper
Knows the annotation exists and that it relates to lambdas; can say two abstract methods cause an error.
States precisely that the sole effect is a compile-time single-abstract-method check, that it is optional, and that two abstract methods is a hard compile error.
Explains the engineering value (documents intent, freezes the contract, localizes breakage to the interface) and the analogy to @Override; reasons about inherited abstract methods in the count.
Discusses it as an API-evolution safeguard for published libraries — guaranteeing source/behavioral compatibility for lambda call sites — and the trade-offs of annotating vs not in a long-lived public API.
## What an annotation is An **annotation** in Java is metadata you attach to code (`@Something`) that tools or the compiler can read. Most annotations do nothing at runtime by themselves; their meaning comes from whatever processes them. `@FunctionalInterface` is processed by the **compiler**. ## The single effect of @FunctionalInterface When you write `@FunctionalInterface` on an interface, the compiler performs one extra validation: it counts the **abstract methods** and requires the count to be **exactly one**. (Recall: `default` methods, `static` methods, and re-declared `public` methods of `Object` such as `equals`/`hashCode`/`toString` do **not** count.) Three outcomes: - **Exactly one abstract method** → compiles fine. - **Zero abstract methods** → compile error ("no abstract method found"). - **Two or more abstract methods** → compile error ("multiple non-overriding abstract methods found"). ```java @FunctionalInterface interface TwoJobs { void a(); void b(); // COMPILE ERROR: multiple non-overriding abstract methods } ``` This is the *entire* behavior. It does not change bytecode, it does not affect runtime, and it does not make the interface "more functional" — an interface is functional based on having one abstract method, annotation or not. ## Optional but valuable Because it is optional, you might ask why use it. Two reasons: 1. **Documents design intent.** A reader immediately sees "this type is meant to be implemented by a lambda." Compare an interface that just happens to have one abstract method today but was never intended for lambda use. 2. **Freezes the contract.** Suppose a library ships `interface EventHandler { void handle(Event e); }` and thousands of callers pass lambdas: `registry.on(e -> log(e))`. A year later a maintainer adds `void cleanup();` to the interface. Without the annotation this compiles, and now **every** lambda call site breaks (a lambda can no longer satisfy a two-method interface), producing a cascade of errors far from the real cause. With `@FunctionalInterface` on the type, the error appears **at the interface itself** the moment the second abstract method is added — the maintainer is stopped right there. This is exactly analogous to `@Override`: optional, but it turns a silent assumption into a checked invariant and localizes the error. ## Interaction with inheritance The count is over the interface's *effective* abstract methods, including inherited ones. If interface `B extends A` and both declare distinct abstract methods, `B` has two abstract methods and cannot be `@FunctionalInterface`. But if `B` re-declares the *same* method `A` already has (an override with the same signature), that is still one abstract method, and `B` can be functional. ## Summary - One job: compile-time check that there is exactly one abstract method. - Two abstract methods → compile error. - Optional; lambdas work without it. - Real value: documents intent and prevents accidental breakage of all call sites when the interface evolves.
- Is @FunctionalInterface inherited by sub-interfaces?No — the annotation itself is not inherited (it is not @Inherited and applies to a type declaration), and even if a sub-interface adds another abstract method it would no longer be functional. Each interface that wants the check must carry the annotation and satisfy the one-abstract-method rule on its own.
- Does @FunctionalInterface allow an interface with zero abstract methods?No. Zero abstract methods is also a compile error — a functional interface must have exactly one, so an interface composed only of default/static methods cannot be marked @FunctionalInterface.
saying these in an interview costs you the question
- Claiming the annotation is required for lambda usage.
- Thinking it has runtime behavior or changes bytecode.
- Saying two abstract methods only cause a warning — it is a hard compile error.
- Forgetting that inherited abstract methods are included in the count.