What are the rules for default arguments when overriding a function, and why does Kotlin forbid specifying a new default in the override?
answer
- Override must NOT declare defaults
- Defaults inherited from base declaration
- Static default + dynamic dispatch = ambiguity
- Compile error if you re-default in override
- Default belongs to the base contract
basics
~10 sAn overriding function must not declare its own default values; it inherits them from the base. This avoids ambiguity about which default applies when you call through a base-type reference.
solid answer
~40 sWhen you override an open/abstract function, Kotlin requires the override to omit default values — writing `= ...` on a parameter in an override is a compile error ("An overriding function is not allowed to specify default values for its parameters"). The override inherits whatever defaults the base declaration specified (or none). The reason is dispatch ambiguity: a call like base.f() resolves defaults from the **static** type used at the call site, but the actual method invoked is chosen by **dynamic** dispatch (virtual). If the base said b = 1 and an override said b = 2, then b's value when calling through a Base reference would be unclear or surprising. Forbidding override-level defaults guarantees a single, statically-known default per parameter, taken from the declaration site, regardless of the runtime type.
code
kotlin · 9 linesopen class Base { open fun f(x: Int = 10) = x }
class Derived : Base() {
override fun f(x: Int) = x * 2 // inherits default 10, no new default allowed
}
fun main() {
val b: Base = Derived()
println(b.f()) // 20 — default 10 from Base, body from Derived
}go deeper
May only know that overrides repeat the signature; unlikely to know the default rule.
Recalls that overrides can't add defaults but may not explain why.
Explains the static-default vs dynamic-dispatch ambiguity that motivates the rule.
Discusses API contract design, conflicting inherited defaults, and alternatives like template methods.
## The rule When a function is **overridden**, the overriding function **must not** specify default values for its parameters. The override **inherits** the defaults (if any) from the function it overrides. Attempting to write a default in the override is a compile-time error: ```kotlin open class Base { open fun f(x: Int = 10) = x } class Derived : Base() { override fun f(x: Int) = x * 2 // OK: no default here // override fun f(x: Int = 20) ... // ERROR: not allowed to specify default in override } ``` So `Derived().f()` uses the **inherited** default `10`, producing `20`. ## Why the restriction exists: static defaults + dynamic dispatch Default arguments are resolved **statically**, from the **declared type** at the call site, but the body that runs is chosen by **dynamic (virtual) dispatch** based on the runtime type. Consider what would happen if overrides could redefine defaults: ```kotlin open class Base { open fun f(x: Int = 1) = x } class Derived : Base() { override fun f(x: Int = 2) = x } // hypothetical, NOT allowed val b: Base = Derived() b.f() // Which default — 1 (static type Base) or 2 (runtime type Derived)? ``` The value of the omitted `x` would be ambiguous and could differ from the method body actually invoked. Kotlin removes the ambiguity by **only allowing the base declaration to define the default**, so the default is always the one from the statically known declaration, while the *behavior* still dispatches dynamically. ## Multiple inheritance of defaults If a class inherits two functions with the **same signature but conflicting defaults** from different supertypes, and you override to resolve the conflict, the override must not declare a default at all — there is no way to "pick" a default in the override; you simply inherit the (now reconciled) declaration's behavior. This is a corollary of the same rule. ## Practical guidance - Put the default **once**, at the most-base declaration where the parameter is introduced. - Treat the default as part of the **contract** of the base API, not an implementation detail of a subclass. - If you need different defaulting behavior per subtype, model it explicitly (e.g., a protected template method) rather than trying to re-default.
- When you call b.f() on a Base reference pointing to a Derived, where does the default come from and which body runs?The default comes from the statically-known declaration (Base's = 10), while the body that runs is Derived's via dynamic dispatch.
- How do you handle two supertypes declaring the same function with different defaults?You override to resolve the conflict but cannot specify a default in the override; the override simply omits defaults.
saying these in an interview costs you the question
- Claiming you can override and supply a new default
- Saying the runtime type determines the default value
- Believing the override must repeat the base default explicitly
- Confusing default resolution (static) with method dispatch (dynamic)