学習9日目:便利そうなポリモーフィズム。

今日の学習時間。

  • Day:9
  • Today:8h
  • Total:67h

学習内容について。

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

  • ポリモーフィズム
    • ある処理を命令したらクラスが違っても同じように振る舞ってくれる仕組み
      • オーバーライド(override)
      • オーバーロード(overload)

メソッドを再定義「オーバーライド」。

class StudentBase {
    public int studentId;
    public String studentName;
    public String clubName;
    public int vitality;

    StudentBase() {
    }

    StudentBase(int studentId, String studentName, String clubName, int vitality) {
        this.studentId = studentId;
        this.studentName = studentName;
        this.clubName = clubName;
        this.vitality = vitality;
    }

    void introduce() {
        vitality = vitality - 10;
        System.out.println("私は" + studentName + "だよ。");
        System.out.println(clubName + "で活動してる。");
    }

    void greeting() {
        vitality = vitality - 10;
        System.out.println("おはよう!");
    }

    void greeting(String clubName) {
        vitality = vitality - 10;
        System.out.println(clubName + "のみんな、おはよう!");
    }

    void showVitality() {
        vitality = vitality - 10;
        System.out.println("残り体力は" + vitality);
    }
}

class StudentActivity extends StudentBase {
    public String homework;

    StudentActivity() {
    }

    StudentActivity(int studentId, String studentName, int vitality) {
        super.studentId = studentId;
        super.studentName = studentName;
        super.clubName = "パソコン部";
        super.vitality = vitality;
        this.homework = "未完";
    }

    void report() {
        vitality = vitality - 10;
        System.out.println("今日はJavaについて学習しました。");
    }

    @Override
    void introduce() {
        vitality = vitality - 10;
        System.out.println("私の名前は" + studentName + "と申します。");
        System.out.println(clubName + "に所属しています。");
        System.out.println("今日の課題は" + homework + "です。");
    }

    @Override
    void greeting() {
        vitality = vitality - 10;
        System.out.println(super.clubName + "の皆さん" + "おはようございます");
    }
}

パソコン部としての子クラスを作成。親クラスではタメ口の自己紹介だが、子クラス(パソコン部)では新入部員という設定で敬語の自己紹介にしてみた。所属する部活動を引数にせずとも、子クラスで自動的に内容に応じた挨拶ができる。

  • オーバーライド=同じ名前で引数の違うメソッドを定義する
  • 親クラスにあったintroduce()をオーバーライドに。
    • 親クラスにあるメソッドと同じシグネチャ(引数が同じ)で再定義
  • greeting()メソッドも部活動によって挨拶が異なるのでオーバーライド化
class Student02 {
    public static void main(String[] args) {
        StudentActivity sa = new StudentActivity(10, "きき", 190);

        sa.homework = "ポートフォリオ作成";
        sa.introduce();
        sa.greeting();
        sa.report();
        sa.showVitality();
    }
}

/* 実行結果

私の名前はききと申します。
パソコン部に所属しています。
今日の課題はポートフォリオ作成です。
パソコン部の皆さんおはようございます
今日はJavaについて学習しました。
残り体力は150

*/

呼び出し元を実行すると上記のようになる。親クラスに全ての機能を実装するのではなく、子クラスに特有の機能があるのであればオーバーライドで分けたほうが違いが明確になるので便利。

例:武器に攻撃力を与える。

package test;

public class Hero extends Character {
    private Integer weapon;

    public Integer getWeapon() {
        return weapon;
    }

    public void setWeapon(Integer weapon) {
        this.weapon = weapon;
    }

    public Hero() {
    }

    public Hero(String name, Integer hp, Integer offense, Integer defense, Integer weapon) {
        super(name, hp, offense, defense);
        this.weapon = weapon;
    }

    @Override
    public void attack(Character opponent) {
        Integer damage = this.getOffense() + this.getWeapon() - opponent.getDefense();

        if (damage > 0) {
            opponent.setHp(opponent.getHp() - damage);
            System.out.println(this.getName() + " は " + opponent.getName() + " に " + damage + " のダメージを与えた!");
        } else {
            System.out.println("ミス! " + this.getName() + " は " + opponent.getName() + " にダメージを与えられない!");
        }
    }
}

Characterクラスではなく、Heroクラスにのみオーバーライドで武器の強さをメソッドに記述した。もし、Characterクラスに武器の強さを記述してしまうと、Enemyにも武器を持たせる必要があるからである。基本的なRPGでは敵は具体的な武器を持っていない。

注意点として、Characterクラスはカプセル化してあるのでCharacter で定義しているプロパティをHero で使うときは、ゲッターセッターを使わなければならない。

@Overrideはアノテーションと呼ばれるもので付けなくても良いが、付ければ他人から見た時に分かりやすいし、Eclipseがシグネチャの記述ミスを教えてくれる。

同名のメソッドを定義「オーバーロード」。

  • オーバーロード
    • 同じ名前のメソッドを定義できる仕組み
    • 引数の数や型は変えることが前提

オーバーライドについて書いたコードのgreeting()メソッドにオーバーロードを記述している。コンストラクタやメソッドに実際の文字列に限らず変数ごと渡すことができる。オーバーロードと言うとややこしいが、ただ単に複数の同名メソッドを作成するだけ(上書き)。

void greeting() {
        vitality = vitality - 10;
        System.out.println("おはよう!");
    }

    void greeting(String clubName) {
        vitality = vitality - 10;
        System.out.println(clubName + "のみんな、おはよう!");
    }

// 呼び出し元

  sa.greeting();
  sa.greeting(String clubName);
オーバーロード使用時の注意点(シグネチャ)
  • 引数の数を同じにしない
  • 引数が同じ個数でも型(Stringint)が違うのはOK
  • 同じ型しか使っていないけど、個数が違うのもOK

実装を強制させる「抽象クラスと抽象メソッド」。

package object;

public abstract class AbstractStudent {

    public String studentName;

    public abstract void greeting();
}
  • 抽象メソッドはインスタンスを作成できない
    • 宣言のみで処理の中身は記述しない
  • AbstractStudentが受け手側の変数になる

抽象メソッドを使用すると実装されていない場合に警告が出る。複数人で作業する場合などはメソッドの作成し忘れや、オーバーライドミスが発生しやすいので利用すべき。

共通する仕様を定義「インターフェイス」。

  • 抽象メソッドのみを持つクラス
    • 実際の処理は記述しない
      • クラスの使い方や書式のみを定義
  • implementsを使う
    • 継承ではなく実装という
  • インターフェイスを実装したら必ず実行内容を定義
package object;

public interface InterfaceStudent {
    public abstract void greeting();
}
  • 抽象クラス、メソッドとほぼ同じ
  • newGreeting メソッドの引数にInterfaceStudentを使用
    • ポリモーフィズム
      • 代入されているインスタンスの型によって処理結果が変わる
  • 呼び出し側のコード量が減る、重複がなくなる
  • 維持保守管理がしやすくなる
    • 大規模システム

気になった点。

抽象クラスとインターフェイスの違いについて。
  • 抽象クラス=具体的な処理内容を記述せず、メソッド名や引数などの定義だけを宣言する
    • 他のクラスの処理の骨組みを定義
    • 継承関係にある処理の再利用
    • 多重継承できない
    • 「 …である( is-a ) 」の関係
  • インターフェース=クラスに含まれるメソッドの具体的な処理内容を記述せず、変数とメソッドの型のみを定義する
    • クラスで共通する仕様を定義
    • 多重継承できる
    • 「 …を持っている( has-a ) 」の関係

今日の反省と明日の目標。

うーん、今日の内容は難しかった。頭をフル稼働させてたから偏頭痛になったぐらい。でも、オブジェクト指向を理解するのにポリモーフィズムは欠かせないから絶対に理解しないといけないんだよね。保守に便利なものであるということは分かった。なんとなくで進めてると後から痛い目をみるから、明日は実際にコードを自分で書いて実装してみようと思う。

閉じる