未来エンジニア養成所Blog

月単価180万以上のプログラミング講師がプログラミングを皆に楽しんでもらうための情報をお届けします。

【Java】オブジェクト指向の基本問題2-13

title


問題2-13

アスリートを表すAthlete抽象クラスは完成しています。
(変更は禁止です)

具体的なアスリートを表す水泳選手Swimmerクラスおよびマラソン選手MarathonRunnerクラスを作成してください。


【SwimmerおよびMarathonRunnerクラス】

  1. Athlete抽象クラスを継承します。

  2. 種目名を表すString型の定数TYPE(public static finalなフィールドを一般的に定数と言います)を定義します。
    値はそれぞれ「水泳」および「マラソン」です。

  3. コンストラクタは選手名(String型)のみを引数として持ちます。
    選手名インスタンスフィールドに値を設定してください。

  4. 抽象メソッドgetTypeを実装してください。
    戻り値は、種目名定数の値を返すだけで良いです。

  5. それぞれの種目に応じたメソッドを作成します。Swimmerクラスは泳ぐことを表すswimメソッドを、MarathonRunnerクラスは走ることを表すrunメソッドを作成してください。
    どちらのメソッドも引数・戻り値なしです。
    出力内容は下記実行結果を参考にしてください。

また、AbstractCastPracticeクラスのmainメソッドは一部未完成です。

アスリート型配列をfor文でループし、その配列要素の実際の型がSwimmerクラスであれば泳ぐswimメソッドを、実際の型がMarathonRunnerクラスであれば走るrunメソッドを実行してください。


【実行結果】

私の名前は北島光一、水泳の選手です。
私の名前は高橋昭子、マラソンの選手です。
私の名前は岩崎正子、水泳の選手です。
私の名前は野口みきえ、マラソンの選手です。

北島光一は泳ぎました。
高橋昭子は走りました。
岩崎正子は泳ぎました。
野口みきえは走りました。


【AbstractCastPractice.java】

public class AbstractCastPractice {
    public static void main(String[] args) {

        // アスリート配列オブジェクトの生成
        Athlete[] athlete = {
                new Swimmer("北島光一"),
                new MarathonRunner("高橋昭子"),
                new Swimmer("岩崎正子),
                new MarathonRunner("野口みきえ")
        };

        // 自己紹介
        for(int i = 0; i < athlete.length; i++) {
            System.out.println(athlete[i]);
        }

        System.out.println();


        // ここから作業してください
        // アスリートの種目に応じたメソッドの実行


    }
}


// Athlete抽象クラス
abstract class Athlete {

    // 選手名
    protected String name;

    // 種目名取得メソッド(抽象メソッド)
    public abstract String getType();

    @Override
    public String toString() {
        return "私の名前は" + name + "、" + getType() + "の選手です。";
    }
}


// ここにSwimmerクラスを作成してください


// ここにMarathonRunnerクラスを作成してください


解答例

【AbstractCastPractice.java】

public class AbstractCastPractice {
    public static void main(String[] args) {

        // アスリート配列オブジェクトの生成
        Athlete[] athlete = {
                new Swimmer("北島光一"),
                new MarathonRunner("高橋昭子"),
                new Swimmer("岩崎正子"),
                new MarathonRunner("野口みきえ")
        };

        // 自己紹介
        for(int i = 0; i < athlete.length; i++) {
            System.out.println(athlete[i]);
        }

        System.out.println();

        // アスリートの種目に応じたメソッドの実行
        for(int i = 0; i < athlete.length; i++) {
            if(athlete[i] instanceof Swimmer) {
                // 泳ぐメソッドの実行
                ((Swimmer)athlete[i]).swim();
            } else if(athlete[i] instanceof MarathonRunner) {
                // 走るメソッドの実行
                ((MarathonRunner)athlete[i]).run();
            } else {
                // 現状はここを通らない
                System.out.println("unknown");
            }
        }

    }
}


// Athlete抽象クラス
abstract class Athlete {

    // 選手名
    protected String name;

    // 種目名取得メソッド(抽象メソッド)
    public abstract String getType();

    @Override
    public String toString() {
        return "私の名前は" + name + "、" + getType() + "の選手です。";
    }
}


// Swimmerクラス
class Swimmer extends Athlete {

    // 種目名
    public static final String TYPE = "水泳";

    // コンストラクタ
    public Swimmer(String name) {
        this.name = name;
    }

    // 種目名取得メソッドの実装
    public String getType() {
        return TYPE;
    }

    // 泳ぐメソッド
    public void swim() {
        System.out.println(name + "は泳ぎました。");
    }
}

// MarathonRunnerクラス
class MarathonRunner extends Athlete {

    // 種目名
    public static final String TYPE = "マラソン";

    // コンストラクタ
    public MarathonRunner(String name) {
        this.name = name;
    }

    // 種目名取得メソッドの実装
    public String getType() {
        return TYPE;
    }

    // 走るメソッド
    public void run() {
        System.out.println(name + "は走りました。");
    }
}


解説

方の扱いを変更するには「キャスト」を行います。

Javaでは、型は大きく分けて2種類あります。

「基本データ型」「参照型」ですが、それぞれにキャストがあります。


今回の問題は、参照型のキャストがテーマであり非常に重要です。

しっかり理解しておきましょう。



アスリート抽象クラスを継承した具体的なクラス(具象クラスといいます)SwimmerおよびMarathonRunnerクラスは問題なく完成できたでしょうか。

スーパークラスであるAthlete抽象クラスの選手名インスタンスフィールドnameがprotectedアクセスなので、サブクラスからは普通に使用することができます。


あとは種目名取得メソッドgetTypeを実装し、Swimmerクラスには泳ぐswimメソッド、MarathonRunnerクラスには走るrunメソッドをそれぞれ定義します。



今回の問題は、mainメソッドの記述がテーマです。

少し難しかったかもしれません。


2人の水泳選手と2人のマラソン選手を、Athlete配列型としてまとめて扱っています。

これが継承の最大のメリットである「グループ化」です。

具体的な「水泳選手オブジェクト」や「マラソン選手オブジェクト」を、スーパークラスである「アスリート型」として扱っているわけです。



このため、ひとつ重大な問題が発生します。


水泳選手は泳ぐメソッドを持ち、マラソン選手は走るメソッドを持つわけですが、「1人のアスリート」が水泳選手なのかマラソン選手なのかを判定する必要があるのです。

そのためには「instanceof」というキーワードを使って、安全にキャスト可能かどうかを判定します。

instanceof演算子は次のように使用します。

if(参照変数 instanceof クラス名) {
    ....
}



instanceof演算子はbooleanの値を返し、参照変数がそのクラスでダウンキャスト可能であればtrueを返します。


たとえば、アスリート型参照変数は水泳選手クラス型に自由にキャストできますが(アスリートクラスと水泳選手クラスには継承関係があるため)、もし実際にそのアスリートがマラソン選手の場合は非検査例外ClassCastExceptionをスローします。

参照型をダウンキャストする際には、必ずinstanceof演算子を用いて安全にキャスト可能か調べてください。



実は、ポリモフィズムを意識した正しいクラス設計を行うと、instanceof演算子は基本的に使わなくてよくなります。


しかし、場合によっては今回のようなダウンキャストの必要性も出てくる場面がありますので、instanceof演算子の使い方はしっかり理解しておきましょう。


参考図書



LINE公式アカウント

仕事が辛くてたまらない人生が、仕事が楽しくてたまらない人生に変わります。
【登録いただいた人全員に、無料キャリア相談プレゼント中!】


LineOfficial

友だち追加