Kotlinにおける override は、単なる文法要素ではありません。
これは 「継承を安全かつ意図的に行うための設計メカニズム」 そのものです。
Javaなどの従来言語と比べると、Kotlinの override はかなり厳格ですが、それには明確な理由があります。
overrideの本質的な役割
override は次の意味を持ちます。
「このメンバーは、親クラスまたはインターフェースで定義されたものを、意図的に上書きしている」
Kotlinでは 偶然の上書き や 設計意図の不明瞭さ を強く嫌うため、override を成立させるには 親と子の両方が明示的に同意 する必要があります。
基本ルール:open と override はセット
親クラス側:open(または abstract)が必要
open class Animal {
open fun speak() {
println("Animal sound")
}
}
Kotlinでは、
- クラス
- 関数
- プロパティ
は すべてデフォルトで final です。
つまり、何も書かなければ継承・override不可 です。
なお
abstractは暗黙的にopenなので、override 可能(かつ必須)になります。
子クラス側:override は必須
class Dog : Animal() {
override fun speak() {
println("Woof")
}
}
override を書き忘れると 必ずコンパイルエラー になります。
これは Kotlin の安全設計の中核です。
Kotlinがoverrideを強制する理由(Javaとの違い)
Javaでは、親と同じシグネチャのメソッドを書くだけで override できてしまいます。
class Child extends Parent {
void foo() {} // overrideかどうか分かりにくい
}
これにより、
- タイポでも override される
- 親の仕様変更に気づきにくい
- 可読性・保守性が低下する
といった問題が起きます。
Kotlinはこれを防ぐため、
- 親:「拡張してよい(open)」
- 子:「上書きする(override)」
という 双方向の契約 を強制しています。
overrideできる対象
関数(メソッド)
open class Base {
open fun hello() {}
}
class Derived : Base() {
override fun hello() {}
}
プロパティ(val / var)
open class User {
open val name: String = "Guest"
}
class Admin : User() {
override val name: String = "Admin"
}
val → var は可能、逆は不可
open class A {
open val x: Int = 1
}
class B : A() {
override var x: Int = 2 // OK
}
理由var は getter + setter を持つため、val の上位互換だからです。
プロパティoverrideで重要な「型ルール」
val の場合:共変(サブタイプOK)
open class A {
open val x: Number = 1
}
class B : A() {
override val x: Int = 2 // OK
}
読み取り専用なので、安全に型を狭められます。
var の場合:基本は同一型(不変)
open class A {
open var x: Number = 1
}
class B : A() {
override var x: Number = 2 // OK
// override var x: Int = 2 // NGになりやすい
}
setter が存在するため、型を変えると安全性が崩れます。
インターフェースとoverride
インターフェースのメンバーは override 可能
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) {
println(message)
}
}
デフォルト実装がある場合
interface I {
fun f() {
println("default")
}
}
class C : I // override不要(defaultが使われる)
- 実装なし → override 必須
- デフォルト実装あり → override 任意
super を使った拡張
open class Animal {
open fun speak() {
println("Animal sound")
}
}
class Dog : Animal() {
override fun speak() {
super.speak()
println("Woof")
}
}
親の処理を活かしつつ振る舞いを追加できます。
final override:これ以上overrideさせない
open class Base {
open fun foo() {}
}
open class Middle : Base() {
final override fun foo() {}
}
class Child : Middle() {
// override fun foo() {} // エラー
}
フレームワークや基盤クラスで非常によく使われます。
アクセス修飾子とoverride
可視性は「広げる」方向のみ可能
open class Base {
protected open fun foo() {}
}
class Derived : Base() {
public override fun foo() {}
}
狭めるのは不可
open class Base {
public open fun foo() {}
}
class Derived : Base() {
protected override fun foo() {} // エラー
}
複数インターフェースでのoverride
interface A {
fun f() { println("A") }
}
interface B {
fun f() { println("B") }
}
class C : A, B {
override fun f() {
super<A>.f()
super<B>.f()
}
}
同名・同シグネチャの実装が複数ある場合、override は必須 になります。
overrideは「設計力」が問われる機能
Kotlinで open や override を使うことは、
- 拡張ポイントの明示
- 責務分離
- 将来変更への耐性確保
を意味します。
典型的な設計例(Template Method)
open class PaymentService {
fun pay(amount: Int) {
validate(amount)
execute(amount)
}
protected open fun execute(amount: Int) {}
private fun validate(amount: Int) {}
}
- override してよい部分
- してはいけない部分
が明確になります。
まとめ
- Kotlinでは override は 必須
- 親は
open/abstract - 子は
override - デフォルトは final
- プロパティには 型ルール がある
- interface の default 実装は override 任意
- override は設計意図を表す重要な仕組み
以上、Kotlinのoverrideについてでした。
最後までお読みいただき、ありがとうございました。










