Why can an interface re-declare a public method of Object (like equals) and still qualify as a functional interface?
answer
- Every implementer is an Object → equals/hashCode/toString already concrete
- Lambda can't express equals/hashCode/toString anyway
- Comparator re-declares equals only for stronger Javadoc contract
- Exemption matches the exact public Object signature
- A near-miss signature (e.g. toString(int)) DOES count
basics
~20 sEvery 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.
solid answer
~50 sThe functional-interface rule counts abstract methods, but it explicitly excludes any abstract method whose signature matches a public method of java.lang.Object — equals, hashCode, toString, and so on. The reason is that every class that could implement the interface is ultimately a subclass of Object and therefore already inherits a concrete implementation of those methods. So even if the interface re-declares equals(Object) as abstract, no implementing class actually has to provide it; Object's version satisfies it. A lambda couldn't supply equals anyway, because a lambda's identity, equality, and string form are defined by the runtime, not by the lambda body. Comparator is the canonical example: it declares compare and also re-declares equals, yet it is a valid functional interface because only compare is a counting abstract method. This exemption lets API designers re-declare Object methods (usually to attach Javadoc specifying a stricter contract) without disqualifying the type from lambda use.
go deeper
Aware that some special methods like equals don't count toward the single-method rule, even if they can't fully explain why.
Can state the rule that re-declared public Object methods are exempt and give Comparator as an example.
Explains the dual rationale — implementations already inherit a concrete version from Object, and a lambda cannot express these methods — and notes Comparator re-declares equals only to strengthen its documented contract.
Reasons about the spec-level signature-matching boundary (near-miss signatures count), the API-design use of re-declaring Object methods for contract documentation, and the interaction with how lambdas synthesize identity-based equals/hashCode.
## Background you need In Java, **every** class implicitly extends `java.lang.Object`, the root of the class hierarchy. `Object` provides concrete (non-abstract) implementations of several `public` methods: `equals(Object)`, `hashCode()`, `toString()`, plus `getClass`, `notify`, `notifyAll`, and the `wait` overloads. So any object you ever create already *has* working versions of all of these. An interface is **functional** when it has exactly one **abstract** method (the SAM). The language spec carves out an exception: an abstract method declared in the interface **does not count** toward the SAM total if it has the **same signature as a public method of `Object`**. ## Why the exemption is necessary and correct Consider what "abstract method" normally means: a method an implementing class is obligated to provide a body for. But for an `equals(Object)` re-declared in an interface, that obligation is **already satisfied** — every implementing class is an `Object` subclass and inherits `Object.equals`. There is nothing left to implement. Treating it as a counting abstract method would be illogical, because it is not actually abstract from the implementer's standpoint: a concrete implementation always exists. Further, lambdas reinforce why this matters. A lambda such as `(a, b) -> a - b` is the body of the *one* SAM. You **cannot** express `equals`, `hashCode`, or `toString` with a lambda — the runtime synthesizes a class for the lambda with its own identity-based `equals`/`hashCode` and a system `toString`. If `equals` counted as a second abstract method, you could never satisfy a `Comparator`-like interface with a lambda, which would defeat the entire purpose. The exemption keeps these types lambda-compatible. ## The canonical example: Comparator ```java @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); // the single counting SAM boolean equals(Object obj); // re-declared Object method — does NOT count // ... plus many default and static methods (reversed, thenComparing, ...) } ``` Why does `Comparator` bother re-declaring `equals` at all, since `Object` already supplies it? To **attach a stronger contract via Javadoc**: `Comparator.equals` documents that two comparators should be considered equal only if they impose the same ordering. The re-declaration is purely a documentation hook for a refined semantic contract; it adds no implementation burden and, thanks to the exemption, does not disqualify `Comparator` from being functional. ## A subtle boundary The exemption only applies to methods that match a `public` `Object` method **signature**. A method that merely *looks* similar but has a different signature is a normal abstract method and counts. For example: ```java @FunctionalInterface interface Bad { void run(); String toString(); // matches Object.toString -> exempt, OK so far int toString(int x); // DIFFERENT signature -> a real abstract method -> now TWO SAMs -> error } ``` The first `toString()` is exempt; `toString(int)` is a distinct signature not present on `Object`, so it counts, giving two abstract methods and a compile error. ## Summary - Implementations are always `Object` subclasses, so re-declared `equals`/`hashCode`/`toString` are already concretely provided — nothing to implement. - A lambda fundamentally cannot supply these methods anyway. - Therefore the SAM count excludes any abstract method matching a public `Object` method. - `Comparator` exploits this to re-declare `equals` for documentation while staying functional. - The match must be on the exact public `Object` signature; a near-miss signature counts as a real SAM.
- Why does Comparator re-declare equals at all if Object already provides it?To attach a refined contract through Javadoc: Comparator documents that two comparators are equal only if they impose the same ordering. The re-declaration is a documentation hook; it adds no implementation requirement and, being an Object-method match, does not count toward the SAM total.
- Would adding an abstract method int toString(int x) to a functional interface keep it functional?No. toString(int) has a different signature from Object.toString(), so it is a genuine abstract method and counts. Combined with the existing SAM, that is two abstract methods, which is a compile error if the type is @FunctionalInterface.
saying these in an interview costs you the question
- Saying re-declaring equals turns the interface non-functional.
- Thinking you can implement equals/hashCode with a lambda.
- Assuming any method named toString is exempt regardless of signature.
- Believing private or protected Object methods (like clone) get the same exemption as the public ones.