skip to content

Diamond Resolution (super<T>)

When two interfaces supply conflicting default implementations, the compiler makes you override and pick using super<Interface>.method(). It is Kotlin's explicit answer to the diamond problem, and a very common interview question.

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

questions

5

A class implements two interfaces that both provide a default implementation of the same method foo(). What does Kotlin require you to do, and why won't it compile otherwise?

level: juniorimportance: must knowfreq 55%

answer

  1. Two default bodies -> must override
  2. Compiler won't auto-pick
  3. super<Interface>.foo() to qualify
  4. Plain super.foo() is ambiguous = illegal
  5. No C3 linearization in Kotlin

basics

~10 s

Both interfaces give a body for the same method, so Kotlin can't pick one for you. You must override the method in your class. Otherwise the compiler reports an error and refuses to build.

solid answer

~40 s

When two supertypes provide a default implementation for the same method signature, Kotlin cannot silently choose one — it forces you to resolve the ambiguity by overriding the member in the implementing class. If you don't, you get a compile error like 'Class C must override foo() because it inherits multiple implementations of it'. Inside your override you decide the behavior, optionally delegating to one or both parents with the qualified super-call syntax super<A>.foo() and super<B>.foo(). The unqualified super.foo() is illegal here because it's ambiguous which supertype it refers to. This is Kotlin's answer to the classic 'diamond problem': no automatic linearization (unlike some languages), explicit resolution required.

code

kotlin · 11 lines
kotlin
interface A { fun foo() { println("A") } }
interface B { fun foo() { println("B") } }

class C : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
}

fun main() { C().foo() } // prints A then B

go deeper

for a junior

Knows that overriding is required and that the build fails otherwise.

for a middle

Can write the override using super<A>.foo()/super<B>.foo() correctly and explains the ambiguity.

for a senior

Articulates that Kotlin deliberately avoids implicit linearization and forces explicit resolution; knows the no-conflict case.

for a principal

Frames it as a language-design trade-off (explicitness vs. convenience) and contrasts with MRO/linearization approaches.

## The diamond problem The 'diamond' happens when a type inherits the **same method** from **two different supertypes**, and each supertype supplies its own implementation. The name comes from the inheritance shape: a top interface, two interfaces extending/declaring it, and one class at the bottom implementing both — forming a diamond. In Kotlin, **interfaces can have method bodies** (default implementations). So two interfaces can each provide a working `foo()`. When one class implements both, the compiler asks: *which `foo()` does the class inherit?* It refuses to guess. ## What Kotlin requires Kotlin does **not** auto-pick a winner and does **not** do C3 linearization. Instead it issues a compile error and **forces you to override** the conflicting member: ```kotlin interface A { fun foo() { println("A.foo") } } interface B { fun foo() { println("B.foo") } } // Won't compile without overriding foo(): class C : A, B { override fun foo() { super<A>.foo() // qualified super call -> A's impl super<B>.foo() // qualified super call -> B's impl } } ``` The error text is roughly: *"Class 'C' must override public open fun foo(): Unit because it inherits multiple implementations of it."* ## Key keywords / mechanics - **`override`** — mandatory on the resolving member. - **`super<A>.foo()`** — the *qualified super* call; the angle-bracket type names exactly which parent's implementation to invoke. - **plain `super.foo()`** — illegal here; it's ambiguous, so the compiler rejects it. - You are free to call **one**, **both**, or **neither** parent — the override fully owns the behavior. ## When there's NO conflict If only one supertype provides a body (the other declares the method abstract, with no body), there is **no diamond** — the single implementation is simply inherited and no override is required.

  • What if only interface A has a body and B declares foo() abstract?
    No conflict — A's implementation satisfies B's abstract member, so the class inherits it and no override is required.
  • Is plain super.foo() allowed inside the override?
    No. With multiple candidate supertypes it's ambiguous; you must qualify with super<A>.foo() or super<B>.foo().

Two parents hand you conflicting instructions; Kotlin won't choose for you — you must write your own note and may quote either parent by name.

saying these in an interview costs you the question

  • Claiming Kotlin silently picks the first interface listed
  • Saying it just works without an override
  • Thinking plain super.foo() resolves the ambiguity
  • Confusing this with Java's actual runtime behavior
  • Believing Kotlin does C3/MRO linearization like Python

context

open as a page

Inside an overriding method, how do you call a specific parent's implementation when several supertypes are involved? Show the exact syntax and explain how the target is chosen.

level: middleimportance: must knowfreq 50%

basics

~10 s

Use super<TypeName>.method(). The name in angle brackets is the exact parent whose version you want to run. You can call several parents this way, in any order, from inside your override.

open as a page

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.

level: middleimportance: should knowfreq 38%

basics

~20 s

You'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.

open as a page

Two interfaces both provide a default getter for the same property. How does diamond resolution apply to properties, and what limitation around interface state should you keep in mind?

level: seniorimportance: should knowfreq 25%

basics

~20 s

The same rule applies: if two interfaces give a body (a getter) for the same property, you must override it and can pick a parent's getter with super<Type>.prop. Interfaces can't hold real fields, so there's no stored state to clash — only the accessor logic.

open as a page

How does Kotlin's diamond resolution interoperate with Java default methods, and when would you reach for super<Type>.foo() in real design (e.g. mixin/trait-style composition) versus restructuring the hierarchy?

level: principalimportance: nice to knowfreq 15%

basics

~20 s

Java default methods behave like Kotlin interface defaults, so the same 'override and qualify' rule applies when a Kotlin class mixes them. In design, use super<Type>.foo() to deliberately combine small reusable behaviors; if conflicts pile up, it's a sign to rethink the hierarchy.

open as a page