Contrast how Kotlin resolves an overridden open member function versus an extension function declared on the same hierarchy. Show what changes when only one of them is an extension.
answer
- Member override = runtime class
- Extension = declared type
- Member wins over same-signature extension
- Extensions can't override, only shadow
- Don't swap member<->extension blindly
basics
~10 sOverridden members run based on the real object (virtual dispatch). Extensions run based on the variable's declared type (static). So members can be polymorphic; extensions cannot.
solid answer
~40 sAn `open` member function that a subclass `override`s is resolved by **virtual dispatch**: at runtime the JVM uses the object's actual class to pick the most-derived override, so `Animal` typed variable holding a `Dog` calls `Dog`'s override. An **extension** is resolved **statically** from the declared receiver type, so the same `Animal`-typed variable always calls the `Animal` extension regardless of the real object. The practical consequence: if you build an API on overridable members you get polymorphism; if you build it on extensions you lock behavior to the declared type. Mixing them is confusing — converting a member to an extension (or vice versa) silently changes dispatch semantics. Extensions also can't be `override`n; a more specific extension only **shadows** a less specific one within a given declared type, never overrides it.
code
kotlin · 11 linesopen class Animal { open fun memberSpeak() = "animal-member" }
class Dog : Animal() { override fun memberSpeak() = "dog-member" }
fun Animal.extSpeak() = "animal-ext"
fun Dog.extSpeak() = "dog-ext"
fun main() {
val pet: Animal = Dog()
println(pet.memberSpeak()) // dog-member (virtual)
println(pet.extSpeak()) // animal-ext (static)
}go deeper
Can state members are virtual and extensions are static, even if shaky on precedence details.
Correctly predicts both outputs in the experiment and knows member-vs-extension precedence.
Explains the bytecode mechanism, the shadowing-vs-overriding distinction, and the refactoring hazard of swapping them.
Sets team conventions about when to expose behavior as members vs extensions to keep dispatch semantics predictable across an API surface.
## Two resolution mechanisms **Virtual dispatch (members):** A function marked `open` and `override`n is stored in the class's virtual method table. At runtime the JVM looks at the object's **actual class** and calls the most-specific override. This is polymorphism. **Static resolution (extensions):** An extension compiles to a static function taking the receiver as a hidden first parameter. The compiler binds the call using the **declared (compile-time) type** of the receiver expression. No runtime lookup occurs. ## Side-by-side experiment ```kotlin open class Animal { open fun memberSpeak() = "animal-member" } class Dog : Animal() { override fun memberSpeak() = "dog-member" } fun Animal.extSpeak() = "animal-ext" fun Dog.extSpeak() = "dog-ext" fun main() { val pet: Animal = Dog() println(pet.memberSpeak()) // "dog-member" -> virtual, uses runtime class println(pet.extSpeak()) // "animal-ext" -> static, uses declared type } ``` Same variable, same object — opposite outcomes. The member call sees the `Dog`; the extension sees the `Animal`. ## What happens when a member and an extension collide If a class declares a **member** function with the same signature as an in-scope **extension**, the **member always wins** (member-vs-extension precedence). The extension becomes effectively unreachable through normal dot calls on that type. This is another reason mixing the two is risky: adding a member later can silently shadow an extension your code relied on. ## Extensions can't override ```kotlin // fun Dog.extSpeak() does NOT override fun Animal.extSpeak(); // it only shadows it when the declared type is Dog. ``` There is no `override` keyword for extensions, no late binding, no super-call chain. ## Design takeaway - Need polymorphic, subtype-aware behavior -> use **open members**. - Need a convenience helper that doesn't pollute the type and behaves uniformly per declared type -> use an **extension**, and document that it is not virtual. - Don't silently convert one into the other; dispatch semantics change.
- A class has both a member `fun describe()` and an in-scope extension `fun MyType.describe()`. Which is called and why?The member. Members take precedence over extensions with the same signature; the extension is shadowed for dot calls on that type.
- Can `fun Dog.extSpeak()` use `super` to call `fun Animal.extSpeak()`?No. Extensions have no override/super mechanism. You'd have to call the function with an `Animal`-typed receiver explicitly.
saying these in an interview costs you the question
- Saying extensions can override members
- Claiming the extension wins over a same-signature member
- Treating extension dispatch as polymorphic
- Not knowing members use the runtime class while extensions use declared type
- Asserting extensions appear in the vtable