skip to content

@FunctionalInterface & SAM Rule

One abstract method makes an interface functional; default and static methods and the public Object methods do not count, and @FunctionalInterface merely asks the compiler to enforce it. Interviewers ask why Comparator still qualifies despite its many methods.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

4

What is a functional interface in Java, and what does the SAM rule require?

level: juniorimportance: must knowfreq 78%

answer

  1. SAM = Single Abstract Method = exactly one
  2. Lambda needs one target method so it knows what to implement
  3. default + static + Object methods don't count
  4. @FunctionalInterface is optional, only a compiler check

basics

~20 s

A functional interface is an interface with exactly one abstract method (the SAM rule, Single Abstract Method). Because there is only one method to implement, you can supply it with a lambda or method reference instead of a full class.

solid answer

~40 s

A functional interface is an interface that declares exactly one abstract method, called the Single Abstract Method or SAM. That single abstract method is the reason a lambda or method reference works: the compiler treats the lambda body as the implementation of that one method, so it knows unambiguously which method you are defining. Examples from the JDK include Runnable (run), Comparator (compare), Callable (call), and the java.util.function types like Function, Predicate, and Supplier. Default methods, static methods, and re-declared public methods of Object (such as equals or toString) do not count toward the single-abstract-method total, so an interface can have many of those and still be functional. The @FunctionalInterface annotation is optional but, when present, makes the compiler verify the interface really has exactly one abstract method.

code

java · 16 lines
java
@FunctionalInterface
interface Greeter {
    String greet(String name);            // the single abstract method (SAM)

    default String greetLoudly(String n) { // default: exempt from the count
        return greet(n).toUpperCase();
    }
    static Greeter friendly() {           // static: exempt
        return n -> "Hi " + n;
    }
    boolean equals(Object o);             // Object method: exempt
}

Greeter g = name -> "Hello " + name;       // lambda implements greet(...)
System.out.println(g.greet("Ada"));        // Hello Ada
System.out.println(g.greetLoudly("Ada"));  // HELLO ADA

go deeper

for a junior

Can state that a functional interface has exactly one abstract method and that this is what lets you pass a lambda. Recognizes Runnable/Comparator as examples.

for a middle

Explains why exactly one method is required (the lambda needs an unambiguous target) and that @FunctionalInterface is an optional compiler check, not a requirement.

for a senior

Articulates the three exempt method categories (default, static, public Object methods) and can reason about why each is exempt, e.g. Object methods are already concretely inherited so a lambda couldn't implement them.

for a principal

Frames functional interfaces as the type-level bridge to lambda/method-reference adaptation, discusses API design implications of marking SAM types, and the maintenance guarantee @FunctionalInterface provides across an evolving library.

## Starting from the ground up An **interface** in Java is a named contract: a list of method signatures (name, parameters, return type) that a class promises to provide. A method is **abstract** when it has a signature but no body — just `int compare(T a, T b);` with a semicolon and no braces. A class that implements the interface must supply the bodies. A **functional interface** is simply an interface that has **exactly one abstract method**. That single method is called the **SAM** — the **Single Abstract Method**. The phrase "SAM rule" just means: to be functional, an interface must have one and only one abstract method. ## Why "exactly one" matters A **lambda expression** is shorthand like `(a, b) -> a - b` and a **method reference** is shorthand like `Integer::compare`. Both are ways to write a tiny anonymous implementation without declaring a class. But for the compiler to accept `(a, b) -> a - b` as an implementation of some interface, it has to know *which method* you are implementing. If the interface had two abstract methods, the compiler could not tell which one your lambda is the body of. With **exactly one** abstract method, there is no ambiguity — the lambda is the body of that one method. This is the entire reason the SAM rule exists. ```java Comparator<Integer> c = (a, b) -> a - b; // lambda IS the body of compare(a, b) ``` Here `Comparator` is functional, its one abstract method is `compare`, and the lambda becomes `compare`'s implementation. ## What does NOT count toward the "one" Three categories of methods are exempt — an interface can have any number of them and still be functional: 1. **`default` methods** — interface methods that *do* have a body (introduced in Java 8), e.g. `Comparator` has `reversed()`, `thenComparing()`, etc. They are not abstract (they have an implementation), so they do not count. 2. **`static` methods** — methods that belong to the interface itself, not to instances, e.g. `Comparator.naturalOrder()`. Not abstract, not counted. 3. **Re-declared `public` methods of `java.lang.Object`** — every interface's implementations are objects, so they already inherit `equals`, `hashCode`, and `toString` from `Object`. If an interface re-declares one of these as abstract (as `Comparator` does with `equals`), it still does NOT count, because any implementing class already has a concrete version inherited from `Object`. So you could not implement it with a lambda anyway. This is why `Comparator` is a valid functional interface even though, reading its source, you see `compare`, `equals`, plus a dozen default and static methods: only `compare` is a counting abstract method. ```java @FunctionalInterface interface Transformer { String apply(String in); // the ONE SAM — counts default String twice(String in) { return apply(apply(in)); } // default — exempt static Transformer identity() { return x -> x; } // static — exempt boolean equals(Object o); // Object method — exempt } ``` ## The @FunctionalInterface annotation `@FunctionalInterface` is an **optional** marker annotation. You do not need it for an interface to be functional — any interface with one abstract method works with lambdas regardless. What the annotation adds is a **compile-time check**: if you mark an interface `@FunctionalInterface` and it has zero or two-or-more abstract methods, the code **does not compile**. It is a guardrail that documents your intent ("this is meant to be used with lambdas") and prevents a future maintainer from accidentally adding a second abstract method and silently breaking every lambda call site. ## Summary - Functional interface = interface with exactly one abstract method (the SAM). - The single method is what a lambda/method reference implements. - `default`, `static`, and `Object` public methods are exempt from the count. - `@FunctionalInterface` is optional; it only turns the rule into a compiler-enforced check.

  • If @FunctionalInterface is optional, why bother adding it?
    It turns intent into a compiler-enforced invariant: the interface won't compile if it ever has zero or more than one abstract method, so a maintainer can't accidentally add a second abstract method and break all the lambda call sites. It also documents that the type is meant for lambdas.
  • Name three functional interfaces from the JDK and their single method.
    Runnable (run), Callable (call), Comparator (compare), Supplier (get), Function (apply), Predicate (test), Consumer (accept) — any one abstract method each.

Think of a lambda as filling in a single blank on a form. If the form has exactly one blank (one abstract method), it is obvious what you are filling in. Default and static methods are pre-printed lines that are already filled in, and the Object methods are signatures everyone already has — none of them are blanks you need to complete.

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).
  • Claiming @FunctionalInterface is required for lambdas to work — it is optional.
  • Thinking default or static methods count toward the single-abstract-method limit.
  • Believing a re-declared equals/toString breaks the functional-interface status.

context

open as a page

What does the @FunctionalInterface annotation do, and what happens if you apply it to an interface with two abstract methods?

level: middleimportance: should knowfreq 62%

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.

open as a page

Why can an interface re-declare a public method of Object (like equals) and still qualify as a functional interface?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Every implementation of an interface is an Object and already inherits concrete equals, hashCode, and toString. So a re-declared Object method is always already implemented and doesn't need a lambda body, which is why it doesn't count as the interface's single abstract method.

open as a page

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%

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.

open as a page