【Kotlin】レッスン5-8:メソッドのオーバーライドの仕組みと使い方

ながみえ

一つ前のページではクラスの継承について学習しました。

今回は メソッドのオーバーライド について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数編
Lesson4:コレクション編
Lesson5:オブジェクト指向編

 ・Lesson5-1:クラスの基本を理解しよう
 ・Lesson5-2:プライマリコンストラクタを理解しよう
 ・Lesson5-3:セカンダリコンストラクタを理解しよう
 ・Lesson5-4:ふたつのコンストラクタを使いこなそう
 ・Lesson5-5:アクセス修飾子とカプセル化を理解しよう
 ・Lesson5-6:静的メンバとインスタンスメンバを理解しよう
 ・Lesson5-7:クラスの継承を理解しよう
 ・Lesson5-8:メソッドのオーバーライドを理解しよう ◁今回はココ
 ・Lesson5-9:クラスを拡張しよう
 ・Lesson5-10:抽象クラスを理解しよう
 ・Lesson5-11:インターフェースを理解しよう
 ・Lesson5-12:データクラスを理解しよう
 ・確認問題5-☆1:モンスター捕獲ゲームを作ろう
 ・確認問題5-☆2:マルバツゲームを作ろう
 ・確認問題5-☆3:石取りゲームを作ろう

<<前のページ

学習記事一覧

次のページ>>

メソッドのオーバーライド|定義・構文・注意点を解説

Kotlinにおいて「メソッドのオーバーライド」は、親クラスで定義されたメソッドを子クラスで再定義し、新たな挙動を与える機能です。

オブジェクト指向の特性を活かし、柔軟で再利用可能なコード設計に役立ちます。

オーバーライドとは何か?|概要とコードの書き方

メソッドの オーバーライド とは、親クラスに定義されたメソッドを子クラス側から再定義することです。

これにより子クラスで特定の処理を上書きし、クラスの振る舞いを変更できます。

オーバーライドを使用するには、親クラスのメソッドが open修飾子 を持ち、子クラスでは overrideキーワード を使用する必要があります。

オーバーライドの基本構文は以下の通りです。

open class 親クラス名 {			// 親クラスの定義
    open fun メソッド名() {		// オーバーライドされることを想定したメソッドの定義
        // このメソッドの処理
    }
}
class 子クラス名 : 親クラス名() {	// 親クラスを継承した子クラスの定義
    override fun メソッド名() {	// メソッドのオーバーライド
        // このメソッドの処理
    }
}

オーバーライドさせるには、それぞれのメソッドが同じ名前である必要があります。

これにより、子クラス側から親クラスのメソッドをオーバーライドする準備が整いました。

オーバーライドの使用コード例と注意点

メソッドのオーバーライドを活用した具体的なコード例を示します。

open class Animal {			// 親クラスとして使用するAnimalクラスの定義
    open fun makeSound() {	// オーバーライドされることを想定したmakeSoundメソッドを定義
        println("Some generic animal sound")
    }
}
class Dog : Animal() {			// Animalクラスを継承したDogクラスを定義
    override fun makeSound() {	// AnimalクラスのmakeSoundメソッドをオーバーライド
        println("Bark!")
    }
}
class Cat : Animal() {			// Animalクラスを継承したCatクラスを定義
    override fun makeSound() {	// AnimalクラスのmakeSoundメソッドをオーバーライド
        println("Meow!")
    }
}

fun main() {
    val animal = Animal()	// 親クラスのインスタンス生成
    val dog = Dog()			// 子クラスDogのインスタンス生成
    val cat = Cat()			// 子クラスCatのインスタンス生成
    animal.makeSound()		// 出力:Some generic animal sound
    dog.makeSound()			// 出力:Bark!
    cat.makeSound()			// 出力:Meow!
}

main関数でそれぞれのクラスのインスタンスを生成し、makeSoundメソッドを呼び出しています。

このとき、親クラスのインスタンスでは親クラスのメソッドが、子クラスのインスタンスではオーバーライドされた子クラス独自のメソッドが実行されます。

共通のインターフェース(メソッド名)を保ちつつ、各クラスごとに異なる動作を実装できるのが継承とオーバーライドの大きなメリットです。

このコードを実行すると以下のように出力されます。

Some generic animal sound
Bark!
Meow!

オーバーライドを使用する際には、以下の点に注意しましょう。

  1. openキーワード: 親クラスのメソッドはデフォルトでオーバーライドが禁止されています。オーバーライド可能にするにはopen修飾子を付ける必要があります。
  2. overrideキーワード: 子クラスでメソッドをオーバーライドする際は、必ずoverrideキーワードを使用します。これにより誤った再定義を防ぎます。
  3. アクセス修飾子: 親クラスと子クラス間で関数の可視性に互換性が必要です。例えば、protected関数をpublicに変更することは可能ですが、privateにすることはできません。

まとめ|オーバーライドで実現する多様なふるまい

オーバーライドは、親クラスで定義されたメソッドを子クラスで自由に再定義できる、継承の大きな強みのひとつです。

これにより、共通のインターフェースを保ちながら、クラスごとに異なるふるまいを実装できます。

オーバーライドを正しく活用することで、拡張性や柔軟性の高い設計が可能になり、
プログラム全体の保守性や再利用性も大きく向上します。

ぜひ今回の内容を参考に、実際の開発でもオーバーライドを積極的に活用してみてください。

練習問題|メソッドのオーバーライドを使った多態性の理解を深めよう

乗り物の動きを表現するプログラムを作成しましょう。

基本となる「乗り物」のクラスを作成し、それを継承して「車」「船」「飛行機」の動きをそれぞれ異なる方法で実装します。

最後にこれらの乗り物の動きを表示するプログラムを作成してください。

この問題の要件

以下の要件に従ってコードを完成させてください。

  1. クラス Vehicle を作成すること。
    • move() という関数を定義し、「乗り物が移動します」と出力すること。
  2. クラス Car を作成し、Vehicle クラスを継承すること。
    • move() 関数をオーバーライドし、「車が道路を走ります」と出力すること。
  3. クラス Boat を作成し、Vehicle クラスを継承すること。
    • move() 関数をオーバーライドし、「船が水上を進みます」と出力すること。
  4. クラス Airplane を作成し、Vehicle クラスを継承すること。
    • move() 関数をオーバーライドし、「飛行機が空を飛びます」と出力すること。
  5. main関数 を作成すること。
    • Vehicle 型のリストを作成し、CarBoatAirplane のインスタンスをリストに追加すること。
    • すべてのインスタンスで move() 関数を呼び出し、動作を表示すること。

ただし、以下のような実行結果となること。

車が道路を走ります
船が水上を進みます
飛行機が空を飛びます

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント1【コードの構成を見る】


正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:Vehicleクラスの定義
  □ moveメソッドの定義(open修飾子付き)
2:Carクラスの定義(Vehicleクラスを継承)
  □ moveメソッドをオーバーライド
3:Boatクラスの定義(Vehicleクラスを継承)
  □ moveメソッドをオーバーライド
4:Airplaneクラスの定義(Vehicleクラスを継承)
  □ moveメソッドをオーバーライド
5:main関数の定義
  □ Vehicle型のリストを作成し、Car、Boat、Airplaneのインスタンスを格納
  □ forループによりリストの各要素について処理
  □ □ moveメソッドを呼び出す

Q
ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

// 基本的な乗り物のクラス
open class Vehicle {
    /*【穴埋め問題1】
    ここにmoveメソッドを定義し、「乗り物が移動します」と出力するコードを書いてください。
    */
}

// 車クラス(Vehicleクラスを継承)
class Car : Vehicle() {
    /*【穴埋め問題2】
    ここにmoveメソッドをオーバーライドし、「車が道路を走ります」と出力するコードを書いてください。
    */
}

// 船クラス(Vehicleクラスを継承)
class Boat : Vehicle() {
    /*【穴埋め問題3】
    ここにmoveメソッドをオーバーライドし、「船が水上を進みます」と出力するコードを書いてください。
    */
}

// 飛行機クラス(Vehicleクラスを継承)
class Airplane : Vehicle() {
    /*【穴埋め問題4】
    ここにmoveメソッドをオーバーライドし、「飛行機が空を飛びます」と出力するコードを書いてください。
    */
}

fun main() {
    // Vehicle型のリストを作成
    val vehicles: List<Vehicle> = listOf(Car(), Boat(), Airplane())

    // 各乗り物に応じた動作を表示
    for (vehicle in vehicles) {
        // 【穴埋め問題5】ここでvehicleオブジェクトのmoveメソッドを呼び出してください。
    }
}

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

この問題の解答と解説

この問題の正解コードとその解説は以下の通りです。

クリックして開いて確認してください。

Q
正解コード
// 基本的な乗り物のクラス
open class Vehicle {
    // 乗り物の走行方法を表す関数
    open fun move() {
        println("乗り物が移動します")
    }
}

// 車クラス(Vehicleクラスを継承)
class Car : Vehicle() {
    // moveメソッドをオーバーライドして車特有の動作を実装
    override fun move() {
        println("車が道路を走ります")
    }
}

// 船クラス(Vehicleクラスを継承)
class Boat : Vehicle() {
    // moveメソッドをオーバーライドして船特有の動作を実装
    override fun move() {
        println("船が水上を進みます")
    }
}

// 飛行機クラス(Vehicleクラスを継承)
class Airplane : Vehicle() {
    // moveメソッドをオーバーライドして飛行機特有の動作を実装
    override fun move() {
        println("飛行機が空を飛びます")
    }
}

fun main() {
    // Vehicle型のリストを作成
    val vehicles: List<Vehicle> = listOf(Car(), Boat(), Airplane())

    // 各乗り物に応じた動作を表示
    for (vehicle in vehicles) {
        // オーバーライドされたメソッドが呼び出されることを確認
        vehicle.move()
    }
}
Q
正解コードの解説

コードをブロックごとに分割して解説します。

クラス Vehicle の定義

open class Vehicle {
    open fun move() {
        println("乗り物が移動します")
    }
}
  • open 修飾子: Kotlinではクラスやメソッドを継承可能にするには open を付ける必要があります。Vehicle クラスは他のクラスに継承されるためopen を指定しています。
  • move メソッド: 基本的な乗り物の動きを表現しています。このメソッドはオーバーライドされるためopen を付けています。

Car, Boat, Airplane クラスの定義と move メソッドのオーバーライド

class Car : Vehicle() {
    override fun move() {
        println("車が道路を走ります")
    }
}

class Boat : Vehicle() {
    override fun move() {
        println("船が水上を進みます")
    }
}

class Airplane : Vehicle() {
    override fun move() {
        println("飛行機が空を飛びます")
    }
}
  • class Car : Vehicle(): クラス CarVehicle を継承しています。: を使用して親クラスを指定します。
  • override 修飾子: 親クラスのメソッドを再定義する際に使います。このコードではそれぞれの乗り物に特有の動きを表現するため、move メソッドをオーバーライドしています。

main 関数でリストを使用して動作を表示

fun main() {
    val vehicles: List<Vehicle> = listOf(Car(), Boat(), Airplane())

    for (vehicle in vehicles) {
        vehicle.move()
    }
}
  • List: Kotlinのコレクション型の一つで複数の要素を保持できます。この場合、Vehicle 型のリストを作成しています。
  • listOf(Car(), Boat(), Airplane()): CarBoatAirplane のインスタンスをリストに追加します。
  • for: リストの各要素に対して繰り返し処理を行います。vehicle.move() によって、それぞれのオーバーライドされた move メソッドが呼び出されます。
Q
サイト改善アンケート & ご指摘/ご質問

本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などを アンケート 形式でお伺いしています。

また記事の内容に関する ご指摘ご質問 もお待ちしています。

1.このサイトをどのように活用していますか?また、今後どのように活用したいですか?
5.気になっているサービス・商品があれば教えてください。
※ 特定の記事に関する内容の場合は、記事番号(レッスン〇-〇)をご記入願います。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました