Can an interface with a generic method, or one inheriting abstract methods from two parents, be a functional interface? How is the single-abstract-method count actually computed?
answer
- Count is over surviving abstract signatures, not the abstract keyword tally
- Merge override-equivalent inherited methods (erasure + covariant return)
- Generic SAM is valid but a lambda can't implement it (lambdas aren't generic)
- Same-signature from two parents → one SAM; different signatures → not functional
- Still drop default/static + Object-method matches first
basics
~20 sYes, a generic method can be the single abstract method. The count is over distinct method signatures after merging override-equivalent ones from all parents. If two unrelated abstract methods survive, the interface is not functional; if inherited methods are override-equivalent they collapse to one and it still is.
solid answer
~50 sThe single-abstract-method count is not a naive tally of declared methods — it is the number of abstract methods with distinct, non-override-equivalent signatures after the interface's full method set (including inherited ones) is computed. A generic method like <T> T pick(T a, T b) is perfectly fine as the SAM; genericity doesn't disqualify it, though such a generic SAM cannot be targeted by a lambda directly because lambdas can't be generic, only by an explicit implementation. When an interface inherits abstract methods from two super-interfaces, the compiler merges override-equivalent ones (same name, same erased parameter types, return-type-compatible) into a single abstract method; if they merge to one, the interface stays functional. If they remain two genuinely distinct abstract methods, it is not functional and @FunctionalInterface would fail to compile. Object-method matches are still excluded from this count, and default/static methods never enter it.
code
java · 14 linesinterface Opener { String act(); }
interface Worker { String act(); } // same signature as Opener.act
@FunctionalInterface
interface Job extends Opener, Worker { } // OK: the two act() merge into ONE abstract method
// Generic SAM: valid functional interface, but NOT lambda-targetable
@FunctionalInterface
interface Picker { <T> T pick(T a, T b); }
// Picker p = (a, b) -> a; // does NOT compile: a lambda cannot be generic
Picker p = new Picker() { // must use an explicit/anonymous implementation
public <T> T pick(T a, T b) { return a; }
};go deeper
Likely only knows the basic 'one abstract method' phrasing; the merge and generic-SAM subtleties are beyond the junior expectation.
Can state that inherited methods count and that default/static are excluded, but may not articulate override-equivalent merging precisely.
Explains that the count is over surviving distinct signatures after merging override-equivalent inherited methods, and recognizes the generic-SAM-vs-lambda limitation.
Reasons at the spec level: method-set computation, erasure-based override-equivalence, covariant return merging, the function descriptor, and the API-design consequence that a technically-functional generic SAM is not lambda-targetable.
## The precise definition The casual phrasing "exactly one abstract method" is correct but glosses over *how the one is counted*. The Java Language Specification defines a functional interface in terms of an **abstract method count after method-set computation**. The steps: 1. Compute the interface's **complete set of methods**, including those inherited from all super-interfaces. 2. Remove every method that is **not abstract** (`default` and `static` methods). 3. Remove every abstract method whose signature **matches a public method of `Object`** (the equals/hashCode/toString exemption). 4. **Merge override-equivalent** abstract methods. Two methods are *override-equivalent* when, roughly, they have the same name and the same parameter types after **type erasure**, and one return type is substitutable for the other. Such methods collapse into a single abstract method. 5. If exactly **one** abstract method remains, the interface is functional; its **function descriptor** is that method. So the question is never "how many `abstract` keywords appear" but "how many distinct abstract method shapes survive the merge." ## Generic methods as the SAM A generic method has its own type parameter, e.g.: ```java @FunctionalInterface interface Picker { <T> T pick(T a, T b); // a generic abstract method } ``` This is a valid functional interface — the genericity does not disqualify it. The subtlety is in **how it can be implemented**. A **lambda cannot be generic**: there is no syntax to introduce a type parameter on a lambda. So you cannot write `(a, b) -> a` and have it be the generic `pick`; the compiler has no place to bind `T`. A generic SAM must therefore be implemented by an explicit class or anonymous class, or the interface is given a *non-generic* SAM if you intend lambda use. This is a classic API-design trap: an interface can be technically functional yet practically un-lambda-able. ## Inheriting from two parents ```java interface A { String run(); } interface B { String run(); } @FunctionalInterface interface C extends A, B { } // functional: the two run() are override-equivalent -> merge to one ``` `A.run()` and `B.run()` are **override-equivalent** (identical signature), so in `C` they merge into a single abstract method. `C` is functional. Contrast: ```java interface A { String run(); } interface B { int run(); } // same name, incompatible return type interface C extends A, B { } // COMPILE ERROR even without @FunctionalInterface — unrelated/incompatible ``` Here the two `run` declarations are not override-equivalent (return types `String` vs `int` are not substitutable), which is an outright inheritance error. And with two genuinely **different** methods: ```java interface A { void open(); } interface B { void close(); } @FunctionalInterface interface C extends A, B { } // ERROR: two distinct abstract methods -> not functional ``` `open` and `close` cannot merge, so `C` has two abstract methods and the annotation rejects it. ## Erasure and return-type covariance in the merge The merge uses **erasure** for parameters. Generic super-interface methods can therefore merge even when their parameter types differ only by type arguments, as long as the erased signatures align. The surviving method may take the **most specific return type** (covariant). This is why a sub-interface that narrows a return type (e.g. returning a subtype) still merges rather than adding a second abstract method. ```java interface Source<T> { T get(); } @FunctionalInterface interface StringSource extends Source<String> { @Override String get(); // covariant/identical after substitution -> still ONE SAM } ``` ## Putting it together for an interview The defensible mental model: *"A functional interface has exactly one abstract method **after** removing default/static methods and Object-method overrides, and after merging override-equivalent inherited methods. Generic methods qualify as the SAM but block lambda targeting; multiple-inheritance of the same signature merges to one, while distinct signatures disqualify the type."* That captures the spec without needing to recite it verbatim. ## Summary - Count = abstract methods surviving after dropping non-abstract + Object-method matches + merging override-equivalent ones. - Generic methods can be the SAM but can't be implemented by a lambda (lambdas aren't generic). - Two super-interfaces with the same-signature abstract method merge to one → still functional. - Two distinct abstract methods → not functional. - Merge uses erasure for parameters and allows covariant return narrowing.
- Why can't a lambda implement a generic single abstract method?Because lambda syntax has no place to declare a type parameter — a lambda is not generic. The compiler can't bind the method's type variable from a lambda body, so a generic SAM must be implemented by an explicit or anonymous class even though the interface is technically functional.
- If interface C extends A (with String run()) and B (with int run()), is C functional?No — and it won't even compile. The two run() declarations are not override-equivalent because String and int return types are incompatible, which is an inheritance error independent of @FunctionalInterface.
Counting the SAM is like counting unique guests at a party where some people RSVP'd through multiple invitations: you merge duplicate RSVPs (override-equivalent methods) into one person, ignore the staff who are always there (default/static and Object methods), and only then count heads. One unique guest means it's a functional interface.
saying these in an interview costs you the question
- Counting raw abstract declarations without merging override-equivalent inherited ones.
- Claiming a generic method cannot be a SAM at all (it can; it just can't be a lambda target).
- Thinking two super-interfaces with the same-named method always disqualify the child.
- Believing return-type covariance creates a second abstract method instead of merging.