💬

継承(extends)、多態性(オーバーライド)

に公開

まえがき

chat-gptで作った学習ロードマップをプログラミング初学者が勉強する試みです
ロードマップはchat-gptを使用してますが、学習は公式のチュートリアルや技術系ブログなどを参考にしています
Goの復習も兼ねているのでGoとの違いについても言及します

今回は継承(extends)、多態性(オーバーライド)について内容をまとめていきます

継承とは?

概要

クラスAからクラスBへと派生させることで、クラスAのフィールドとメソッドをクラスBへと継承させること

それぞれの関係性はそれぞれからみて

  1. クラスA
    スーパークラス(基本クラス、親クラスとも呼ぶ)

  2. クラスB
    サブクラス(派生クラス、拡張クラス、子クラスとも呼ぶ)

また全てのクラスはobjectから派生している

目的

同じようなクラスを作成するときにクラスを継承させることで、フィールドやメソッドを書かずに再利用できる
フィールドやメソッドの追加もできるので、サブクラスはスーパークラスを拡張したものといえる

実装方法

最小限の継承
// スーパークラス
class A {
}

// サブクラス
class B extends A {
}

クラス名のあとにextendsキーワードと、継承元のスーパークラスをつけると継承できる

実装例

Animal.java
// スーパークラスのメンバーには全てのAnimalが持つべき要素を記述する
class Animal {

    int x;	// x軸の位置情報
    int y;	// y軸の位置情報

    void getPosition() {
        System.out.print(x + ", " + y);
    } 
  }


// サブクラスのメンバーには種類毎に異なる要素を追加できる
// Animalを継承しているのでフィールド`x, y`やメソッド`getPosition`も使える
class Cat extends Animal {

    void walk() {
        x += 1;
    }
}

class Bird extends Animal {

    void fly() {
        y += 1;
    }
}

共通の機能をAnimalに持たせているので、全てのサブクラスが位置情報フィールドや現在地の出力などを使える
Catは横方向に動くのでx軸の変化
Birdは縦方向に動くのでy軸の変化

多態性(オーバーライド)とは

多態性(ポリモーフィズム)とオーバーライドは別の概念なので見出しを変えようかと思ったが、多態性を実装するにあたってオーバーライドは必須だったので、あえてそのままの見出しで使うことにした

というのも先ほどの継承例で使ったように、メソッドの追加をしただけでは多態性が実現しなかった

オーバーライドとは

概要

継承元(スーパークラス)のインスタンスメソッドと”同じシグネチャ・返り値”を持つサブクラスのインスタンスメソッドは、継承元のインスタンスメソッドをオーバーライドする

端的にいうとスーパークラスのメソッドをサブクラスで上書きする機能のこと

目的

サブクラスごとに独自のメソッドを持たせることができる

実装

オーバーライド
class Animal {

  void makeSound() {
      System.out.print("動物の鳴き声");
  }
}

class Cat extends Animal {
  @Override
  void makeSound() {
      System.out.print("meow");
  }
}

@Overrideアノテーションを付けることが推奨される
アノテーションを付けることで継承元にメソッドが無かったり、シグネチャが違う場合にはコンパイラが教えてくれる

多態性(ポリモーフィズム)とは

概要

多態性=ポリモーフィズムとは共通のインターフェースを通じて、異なるオブジェクトがそれぞれ別の振るまいをする仕組みのこと

目的

コードの柔軟性・再利用性・メンテナンス性が向上する
インスタンスをまとめてスーパークラス型で扱い、それぞれのサブクラスとして振るまわせることができる

実装

継承の「サブクラスはスーパークラスの一種である」という性質を利用する
先ほどの例で言えば「CatAnimalの一種である」ということ

つまりAnimal型のオブジェクトにはCatが代入できる、それはCatとして振るまう
このように上位のクラス型へ変換することをアップキャストという(反対はダウンキャスト)

ポリモーフィズム例
class Animal {
  void makeSound() {
      System.out.print("動物の鳴き声");
  }
}

class Cat extends Animal {
  @Override
  void makeSound() {
      System.out.print("meow");
  }
}

public class Main {
  public static void main(String[] args) {
    // これは通常のインスタンス初期化
    Animal a = new Animal();
    a.makeSound(); // 動物の鳴き声

    // Animal型のオブジェクトだが実体はCatクラス(暗黙的にアップキャストされる)
    Animal neko = new Cat();
    neko.makeSound(); // meow

  }
}

JavaとGoの違い

Goでは継承やオーバーライドの概念は無いので割愛する
埋め込み(embedded)が継承に近いが、Goの埋め込みはJavaでいうところの委譲にあたる

多態性もインターフェースを使って実装するのでここでは割愛する

まとめ

継承、オーバーライド、多態性について調べた

なるべく単純な機能にフォーカスして書こうと思ったが、それぞれが密接していて切り離すのが意外と難しかった
特にオーバーライドを利用した多態性は、Goとは実装方法が違ったので混乱したが思ったよりもシンプルだった

次回はインターフェース(interface)と抽象クラス(abstract)について調べる
それとロードマップに無かったので委譲についても触れたいと思う

初学者なので間違ってる部分があればご指摘いただけると嬉しいです

Discussion