skip to content

A method is overridden somewhere in a class hierarchy. How do you stop further subclasses from overriding it again, and what is the default if you do nothing?

level: seniorimportance: should knowfreq 45%

answer

  1. override is implicitly open
  2. final override = seal the method here
  3. Stops fragile-base-class leaking deeper
  4. abstract override re-abstracts (opposite)
  5. Redundant in a final class

basics

~10 s

By default an overriding method stays open, so deeper subclasses can keep overriding it. To stop that, write final override on the method, which seals it.

solid answer

~40 s

When you `override` an open member, your override is **implicitly open** — Kotlin keeps the extension point open down the chain so further subclasses can override it again. If you want to lock the behavior at a given level, declare it `final override`, which removes the openness while still satisfying the override. This is useful in a deep hierarchy where a mid-level class wants to guarantee its implementation is the final word (preventing the fragile-base-class problem from leaking deeper). Note the interaction with the class modifier: `final override` works only in a class that can still have subclasses (open/abstract); in a fully final class no further override is possible anyway. You can also re-open or keep open by simply writing `override` (default). The keywords in play are `override`, `open`, and `final`.

code

kotlin · 3 lines
kotlin
open class A { open fun f() = "A" }
open class B : A() { final override fun f() = "B" }
// open class C : B() { override fun f() = "C" } // ERROR: f is final in B

go deeper

for a junior

May not know overrides stay open; can at least state final forbids overriding.

for a middle

Knows overrides are implicitly open and that final override seals a single member.

for a senior

Applies final override deliberately to bound an extension point and explains the fragile-base-class motivation.

for a principal

Designs layered hierarchies choosing per-method open/final/abstract to encode invariants and minimize the inheritance contract.

## The implicit-open rule for overrides Kotlin treats an `override` as **implicitly `open`**. So in a chain A → B → C, if A declares `open fun f()` and B does `override fun f()`, then C can still `override fun f()` — B's override did not close it. ```kotlin open class A { open fun f() = "A" } open class B : A() { override fun f() = "B" } // still open open class C : B() { override fun f() = "C" } // allowed ``` ## Sealing with `final override` To make B's implementation the last word, prefix `final`: ```kotlin open class B : A() { final override fun f() = "B" } // sealed here // class C : B() { override fun f() = "C" } // ERROR: f is final in B ``` `final override` says "I am overriding the parent's open member, and I forbid anyone below me from overriding it again." It is the precise tool for controlling how far an extension point propagates. ## Why this matters (design) Leaving every override open enlarges the *inheritance contract*: each level must remain correct under arbitrary re-override. Sealing with `final override` at the layer that owns the invariant shrinks that surface — a deliberate defense against the fragile base class problem, where deep subclasses break when they re-override and bypass invariants. ## Interaction with class openness - In a **final class**, no subclass exists, so `final override` is redundant (members are unoverridable anyway). - In an **open/abstract class**, `final override` is meaningful: subclasses exist but cannot touch that member. - An **abstract override** (`abstract override fun f()`) re-abstracts a member, forcing concrete subclasses to re-implement — the opposite of sealing. ## Summary of modifiers on an override | You write | Effect on deeper subclasses | |---|---| | `override fun f()` | still open (can re-override) | | `final override fun f()` | sealed (cannot re-override) | | `abstract override fun f()` | must re-implement |

  • What does `abstract override fun f()` do?
    It re-declares an inherited member as abstract, forcing every concrete subclass below to provide its own implementation again.
  • Is `final override` ever needed in a final class?
    No. A final class has no subclasses, so its members cannot be overridden regardless; the `final` is redundant there.

saying these in an interview costs you the question

  • Believing an override is final by default
  • Not knowing `final override` exists
  • Confusing `final override` with `abstract override`
  • Thinking the class's final modifier is needed to seal one method

context