When does the diamond actually force an override, and when does it NOT? Walk through the cases of abstract vs. default members across the supertypes.
answer
- Override forced only on >=2 concrete impls
- One body + one abstract = no conflict
- Both abstract = ordinary impl, no super<>
- Most-specific override wins -> no conflict
- Concrete bodies are what collide
basics
~20 sYou're forced to override only when more than one parent supplies a body for the same method. If only one parent has a body (or none do for an abstract member you must implement anyway), there's no conflict to resolve.
solid answer
~40 sThe override is forced only when **two or more supertypes provide concrete implementations** of the same signature. Cases: (1) both A and B have default bodies for foo() -> conflict -> must override. (2) A has a body, B declares foo() abstract -> no conflict; A's body satisfies B; no override needed (though you may override). (3) Both abstract -> not a diamond of *implementations*; the class must implement foo() like any abstract member, but there's nothing to qualify. (4) A common super interface declares foo() and only one descendant overrides it -> the single most-specific implementation wins, no conflict. The compiler's rule is essentially: a non-abstract class must override any member for which it inherits **multiple distinct concrete implementations**.
go deeper
Knows that two bodies cause a conflict needing override.
Correctly distinguishes the one-default-one-abstract and both-abstract cases.
States the precise rule (>=2 distinct concrete impls) and reasons about most-specific-override resolution.
Connects the rule to how interface default methods interact with multiple-interface inheritance and predicts edge cases.
## The decision rule A concrete class must override `foo()` **iff it inherits two or more distinct concrete implementations** of `foo()`. Otherwise no diamond override is forced. ## Case-by-case ### Case 1 — both default (CONFLICT) ```kotlin interface A { fun foo() { /* body */ } } interface B { fun foo() { /* body */ } } class C : A, B { override fun foo() { super<A>.foo() } } // required ``` Two concrete impls -> override mandatory. ### Case 2 — one default, one abstract (NO conflict) ```kotlin interface A { fun foo() { println("A") } } // concrete interface B { fun foo() } // abstract, no body class C : A, B { } // OK! A.foo satisfies B's abstract foo ``` Only **one** concrete implementation exists, so it's inherited unambiguously. No override needed (you may still override). ### Case 3 — both abstract ```kotlin interface A { fun foo() } interface B { fun foo() } class C : A, B { override fun foo() { /* provide impl */ } } ``` There's no *implementation* conflict to resolve. The class must implement `foo()` because it's abstract — ordinary abstract-member rules, not the diamond rule. There is no super<...> to call (no parent body). ### Case 4 — shared base, one specialization ```kotlin interface Base { fun foo() { println("base") } } interface Mid : Base { override fun foo() { println("mid") } } class C : Base, Mid { } // OK: Mid.foo is the single most-specific impl ``` When one implementation **overrides** the other along the hierarchy, the most-derived one is the unique concrete impl; no ambiguity. ## Why it matters Mixing **interface default methods** (concrete bodies in interfaces) with multiple inheritance of interfaces is where this bites. Abstract members never collide because there's nothing to choose between.
- If interface Mid overrides Base.foo and class C implements both Base and Mid, must C override foo?No. Mid.foo is the single most-specific concrete implementation, so it's inherited unambiguously.
- Both interfaces declare foo() abstract. Can you call super<A>.foo() in your impl?No — there's no body in either supertype to call, so there's nothing to qualify; you just write the implementation.
saying these in an interview costs you the question
- Thinking abstract members trigger the diamond override rule
- Assuming any two same-named methods force an override regardless of bodies
- Not knowing a default impl satisfies an abstract member from another interface
- Missing that the most-specific override resolves the conflict
- Claiming you can super-call an abstract (body-less) member