skip to content

In Kotlin, if a class has a member function and you also write an extension function with the exact same name and parameter signature, which one gets called when you invoke it on an instance of that class?

level: juniorimportance: must knowfreq 70%

answer

  1. Member always beats same-signature extension
  2. Extension is shadowed, still compiles, never runs
  3. Different params = separate overload, fine
  4. Class author keeps control
  5. IDE warns 'shadowed by member'

basics

~10 s

The member function always wins. When the name and parameters match, Kotlin calls the function defined inside the class and ignores your extension. The extension is effectively shadowed.

solid answer

~40 s

When a member function and an extension function have the same name and the same parameter signature, the member always takes precedence. The compiler resolves the call to the member; the extension is shadowed and never invoked through normal `instance.foo()` syntax. This is a fixed language rule, not a warning you can override at the call site. The extension still compiles, but it is dead for that signature. The rule exists so that a class author keeps control of its own behavior: adding an extension later cannot silently hijack an existing member. IntelliJ usually flags such an extension as 'never used' / shadowed. The only way the extension runs is via different mechanics (a different signature, or calling it as a plain function), not by hoping it overrides the member.

code

kotlin · 9 lines
kotlin
class Greeter {
    fun hello() = "member hello"
}

fun Greeter.hello() = "extension hello"

fun main() {
    println(Greeter().hello()) // "member hello" — member wins
}

go deeper

for a junior

States the bare rule: member wins, extension is shadowed.

for a middle

Adds that extensions are static, don't modify the class, and that different signatures make a separate overload.

for a senior

Explains the design rationale (class author control, no library hijacking) and how IDE warnings surface it.

for a principal

Frames it as a binary-compatibility/encapsulation guarantee and contrasts with languages where extension-like mechanisms can shadow members unsafely.

## The core rule Kotlin lets you add **extension functions** — functions you declare *outside* a class that you can call as if they were members, e.g. `fun String.shout() = uppercase()` then `"hi".shout()`. A **member function** is one declared *inside* the class body. When a **member** and an **extension** have the **same name and the same parameter signature**, the **member always wins**. The compiler binds the call to the member; the extension is *shadowed* (hidden) for that receiver type. ```kotlin class Greeter { fun hello() = "member hello" } fun Greeter.hello() = "extension hello" fun main() { println(Greeter().hello()) // prints "member hello" } ``` ## Why this rule exists Extensions are **resolved statically** and dispatched by the compiler — they are *not* virtual and they do **not** modify the class. If extensions could override members, then importing an extension from any library could silently change a class's behavior. To keep the class author in control, Kotlin makes the member authoritative. ## What the extension does in this case - It still **compiles**. - It is simply **unreachable** through `instance.hello()` — the call resolves to the member. - The IDE typically warns: *"Extension is shadowed by a member"* / *"never used."* ## The key distinction: same signature vs. different signature The rule only applies when the **signature matches**. If your extension has **different parameters**, it becomes a separate **overload** and is freely callable — overload resolution picks it normally: ```kotlin class Greeter { fun hello() = "member" } fun Greeter.hello(name: String) = "extension $name" Greeter().hello() // member Greeter().hello("Ann") // extension — different signature, no conflict ``` So "member wins" is specifically about **exact-signature collisions**, not about the name alone.

  • If the member wins, is there any way to call the shadowed extension?
    Not through normal member-call syntax on that receiver. You can only reach an extension that doesn't collide — e.g. give it a different signature, a different name, or rely on a smart-cast where no matching member exists.
  • Does the IDE help you notice this?
    Yes — IntelliJ flags the extension as shadowed by a member / never used, but it is only a warning; the code still compiles.

An extension is like a sticky note on a book; if the book already prints that sentence, readers see the printed page, not your note.

saying these in an interview costs you the question

  • Claiming the extension overrides or replaces the member
  • Saying it depends on import order or which is declared last
  • Thinking you get a compile error for the collision
  • Believing extensions are virtual / can be 'overridden'
  • Assuming you can force the extension with an annotation

context