Kotlinのoverrideについて

採用はこちら

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で openoverride を使うことは、

  • 拡張ポイントの明示
  • 責務分離
  • 将来変更への耐性確保

を意味します。

典型的な設計例(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についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次