skip to content

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%

answer

  1. Property getter conflict -> override + super<Type>.prop
  2. Interfaces have no backing fields
  3. Conflict is over getters, never stored values
  4. var: getter and setter both qualify
  5. Real state goes in the class

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.

solid answer

~40 s

Property diamonds resolve exactly like method diamonds. If interfaces A and B both supply a default getter for `tag`, the implementing class must override `tag` and may delegate via `super<A>.tag` / `super<B>.tag` inside its accessor. The crucial difference is **interfaces cannot have backing fields** — they hold no state, only abstract or accessor-based properties. So the 'conflict' is purely about which **computed getter** runs, never about clashing stored values. If you need real state you introduce a backing property in the class itself. For `var` properties, both getter and setter accessors are subject to the same qualified-super resolution. This keeps the diamond purely behavioral, sidestepping the state-collision problems that full C++-style multiple inheritance of state has.

code

kotlin · 7 lines
kotlin
interface Themed { val color: String get() = "black" }
interface Branded { val color: String get() = "blue" }
class Widget : Themed, Branded {
    override val color: String
        get() = super<Branded>.color
}
fun main() { println(Widget().color) } // blue

go deeper

for a junior

Knows properties can also need overriding when two interfaces define them.

for a middle

Writes the override using super<Type>.prop for a getter.

for a senior

Explains the no-backing-field limitation and that conflicts are purely about getters, plus the var/abstract cases.

for a principal

Ties the stateless-interface design to avoiding multiply-inherited-state problems and reasons about the full accessor surface.

## Properties follow the same rule A property in an interface is really an accessor (a `get()` and possibly `set()` for `var`). If two interfaces both provide a **default getter** for the same property name, a class implementing both inherits two concrete accessors -> conflict -> must override. ```kotlin interface Themed { val color: String get() = "black" } interface Branded { val color: String get() = "blue" } class Widget : Themed, Branded { override val color: String get() = super<Themed>.color // qualified super property access } ``` `super<Themed>.color` invokes `Themed`'s getter specifically. ## The state limitation **Interfaces cannot declare backing fields.** An interface property must be either: - **abstract** (`val x: Int`) — the implementor supplies storage, or - **accessor-based** (`val x get() = ...`) — computed, no storage. You cannot write `val x = 5` directly in an interface (that needs a backing field). Therefore a property diamond never involves **two competing stored values** — only two competing **computed getters**. This is deliberate: it avoids the hard problem of multiply-inherited state. ## var and setters For a `var`, both accessors participate: ```kotlin interface A { var n: Int } interface B { var n: Int } class C : A, B { override var n: Int = 0 } // single backing prop resolves both ``` If both were abstract, one backing property in `C` satisfies both. If both supplied concrete accessors, you'd override and qualify each accessor with `super<A>.n` / `super<B>.n` as needed. ## Takeaway Diamond resolution for properties = same `override` + `super<Type>.prop` mechanics as methods, but limited to accessor logic because interfaces are stateless. Real state lives in the class.

  • Why can't a property diamond cause clashing stored values?
    Because interfaces can't have backing fields — their properties are abstract or computed, so there's no state to clash, only getter logic.
  • If both interfaces declare the property abstract, what resolves it?
    A single backing property in the class satisfies both; no qualified super is needed since there are no parent accessors to call.

Two recipe cards (getters) for 'sauce'; you pick whose recipe to follow. There's no pre-made jar of sauce (stored state) to fight over.

saying these in an interview costs you the question

  • Claiming interfaces can hold backing fields / initialized properties
  • Thinking property diamonds differ fundamentally from method diamonds
  • Forgetting that super<Type>.prop accesses a specific parent's getter
  • Believing stored interface state can collide
  • Not handling the var setter case

context