skip to content

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?

level: principalimportance: nice to knowfreq 28%

answer

  1. Count is over surviving abstract signatures, not the abstract keyword tally
  2. Merge override-equivalent inherited methods (erasure + covariant return)
  3. Generic SAM is valid but a lambda can't implement it (lambdas aren't generic)
  4. Same-signature from two parents → one SAM; different signatures → not functional
  5. Still drop default/static + Object-method matches first

basics

~20 s

Yes, 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 s

The 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 lines
java
interface 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

for a junior

Likely only knows the basic 'one abstract method' phrasing; the merge and generic-SAM subtleties are beyond the junior expectation.

for a middle

Can state that inherited methods count and that default/static are excluded, but may not articulate override-equivalent merging precisely.

for a senior

Explains that the count is over surviving distinct signatures after merging override-equivalent inherited methods, and recognizes the generic-SAM-vs-lambda limitation.

for a principal

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.

context