Java 例外の処理方法 〜try catchの使い方とFile入出力〜

イントロダクション

前回は、ポリモーフィズムの実装を行いました。

これは、クラスの継承関係を作り、クラスの型をうまく変換して多様な使い方を行うというものでした。

例えば、下のような使い方をしました。

勇者率いるパーティを表現

public static void main(String[] args) {
        Character[] party = new Character[2];
        party[0] = new Hero("太郎");
        party[1] = new Wizard("二郎");

        System.out.println("こんにちは、良いパーティですね。");
        for (int i = 0; i < party.length; i++) {
            // ここの処理を変更する必要がある
            System.out.println(party[i].getName() + "さん");
        }
}

この実装は、下の図のように、クラスの継承関係作り、Hero, Wizardのクラスを1つのデータ型( クラス型)として使用しています。

Javaプログラムは「型安全」なので、データ型は厳密です。

しかし、このような形でクラスの継承関係を作ると、いろんな組み合わせが可能になります。

このような実装のことを「ポリモーフィズム」と言いました。

例外処理

何かと倦厭されがちな「例外処理」ですが、上のポリモーフィズムの実装は単純なので例外処理がなくても問題ありませんが、これに対して、複雑な組み合わせを行った時、例外処理がないととても不便なのです。

==エラーの種類==

  1. 文法エラー
  2. 実行時エラー
  3. 論理エラー(仮にこう呼ぶ):処理の結果が想定通りでない。

そして、上のようなエラーがあったときにそれぞれの例外を投げます。これは、プログラムの文法で「Throw」という文言を使用するためです。日本語では「投げる」です。

例えば、例外の処理は以下の系統に分けることができます。

  • Error: 通常のアプリケーションであればキャッチすべきではない重大な問題
  • Exception: 通常のアプリケーションでキャッチされる可能性のある状態、クラスExceptionと、そのサブクラスのうちでRuntimeExceptionのサブクラスでないものがすべて、チェック例外になります。
  • RuntimeException: Java仮想マシンの通常の処理でスローすることができる各種の例外のスーパー・クラスです。

エラーがどこで起きたか分からない

稀に、例外が出てもエラーメッセージを見ない人がいますが、ちゃんと見ましょう。「どこでエラーが出ているのか?」はこの例外処理の結果を見ればわかるはずです。

そのように作れば良いのです。

File入出力

例外処理の代表的なものは「ファイル入出力」です。このファイル操作の処理を実装するときに必ず「IOException」が出てきます。ただし、ラップしているような処理は見えてきませんが。。。

そんなわけで、File入出力の実装をして見ましょう。

<プロパティファイルを読み込む(全部)>

private void loadProperties(String fileName) {
    Properties prop = new Properties();
    try {
        Path path = Paths.get("resources", fileName);

        if (isDebug) System.out.println("Path: " + path.getParent().toString() + "\\" + fileName);

        BufferedReader buf = Files.newBufferedReader(path);
        prop.load(buf);
    } catch (IOException ie) {
        System.out.println(fileName + "の読み込み時にエラーがありました。");
        ie.printStackTrace();
        System.exit(-1);
    }
    if (isDebug) System.out.println("propLength: " + prop.size());

    prop.keySet().stream().forEach(key-> {
        // key = 実行クラスの番号
        String className = prop.getProperty(key.toString());
        try {
            Class<CommandIF> klass = (Class<CommandIF>) Class.forName(className);
            clsMap.put(key.toString(), klass);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
    });

    if (clsMap.size() == 0) {
        System.out.println("プロパティファイルにクラスが登録されていません。");
        System.exit(-1);
    }
}

この処理で、プロパティファイルに書いてある、キーから完全クラス名を取得し、クラス・オブジェクトを取得、Mapインターフェースに登録しています。

細かく見ていきますので、ご安心ください

ファイルの読み込み

ファイルの読み込み処理の部分です。以下に抜粋します。

    Properties prop = new Properties();
    try {
        Path path = Paths.get("resources", fileName);

        if (isDebug) System.out.println("Path: " + path.getParent().toString() + "\\" + fileName);

        BufferedReader buf = Files.newBufferedReader(path);
        prop.load(buf);
    } catch (IOException ie) {
        System.out.println(fileName + "の読み込み時にエラーがありました。");
        ie.printStackTrace();
        System.exit(-1);
    }

この部分はfileNameに文字列で「ファイル名」が入っている想定です。

想定というのは、そのようにメソッドを使ってもらう前提ということです。

そして、上から順に、

Properties prop = new Properties();

ここで、プロパティクラスをインスタンス化しています。

Path path = Paths.get("resources", fileName);

この行では、resourceフォルダ内にある「fileName」のパスを取得しています。*ファイルがないときはnullが返ります。

下の処理はデバック用の処理です。取得したパスを文字列で表示しています。

if (isDebug) System.out.println("Path: " + path.getParent().toString() + "\\" + fileName);

そして、ファイルを読み込むためのクラスを取得(インスタンス化)します。

BufferedReader buf = Files.newBufferedReader(path);

最後に、Propetiesクラスで、取得したファイルリーダー(クラス)からプロパティファイルをロードします。

お気付きの方がいるかもしれませんが、ここで読み込もうとしているのは、プロパティファイルです。

ここまでが、処理の説明になります。

例が処理について

例外処理に関しては、try { ... } catch (例外クラス) { ... }で囲まれた、処理を書きます。

具体的には、このように書きます。

    try {
        Path path = Paths.get("resources", fileName);

        if (isDebug) System.out.println("Path: " + path.getParent().toString() + "\\" + fileName);

        BufferedReader buf = Files.newBufferedReader(path);
        prop.load(buf);
    } catch (IOException ie) {
        System.out.println(fileName + "の読み込み時にエラーがありました。");
        ie.printStackTrace();
        // プログラムの強制終了
        System.exit(-1);
    }

このように、ファイルを読み込もうとしたときに、例外が発生する可能性があるので、下のコードはThrows文が書かれています。
<使用している部分>

BufferedReader buf = Files.newBufferedReader(path);

<呼び出しているメソッドの定義>

BufferedReader java.nio.file.Files.newBufferedReader(Path path) throws IOException

なので、throwをキャッチする処理を書かなくてはなりません。

このように、例外が発生する可能性があるのであれば、「throws」文を使い例外が発生する可能性があることを呼び出した側のメソッドに通知する必要があります。

逆にメソッドの内部で揉み消してしまうならば、下のようにtry-catchで処理をしてしまえば良いのです。

    try {
        // 何かしらの処理
    } catch (IOException ie) {
        System.out.println("エラーがありました。");
        ie.printStackTrace();
        // プログラムの強制終了
        System.exit(-1);
    }

実際に作ってみる

まずは、サンプルコードを見てください。実行確認済みです。

public static void main(String[] args) {
    Path path = Paths.get("resources", "mains.properties");
    try {
        BufferedReader read = Files.newBufferedReader(path);
        String line = null;
        while((line = read.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException ie) {
        ie.printStackTrace();
        System.exit(-1);
    }
}

メインメソッドで、ファイル読み込みの処理を行ったものです。

<処理内容>

  1. resourceフォルダ(ビルドパスが通っている)にある「mains.properties」のパスを取得します。
  2. ファイル読み込みクラス、BufferedReaderを作成(インスタンス化)します。
  3. ファイルの内容を1行ずつ、読み込み、読み込み行がないならばnullが返ってくるので処理を終了します。
     ファイルの内容を標準出力に出力している

このような形で、例外処理を行いますが、これを別なkたちで使用することもあります。

チェック時の例外を投げる

<入力チェックの例外>
【前提】
・CheckerUtils#fixStr()は以下のように定義している

public static boolean fixStr(String str) throws Exception {
    if (str == null) {
        throw new Exception("strはnullにできません");
    }
    System.out.println(str);
}

<呼び出し元>

public static void main(String[] arg) {
    try {
        ChecherUtis.fixStr("aaaa");
    } catch (Exception e) {
        e.printStackTrace();
        Systme.exit(-1);
    }
}

このようにすると、例外に「strはnullにできません」と表示することができます。

さらに、Exceptionクラスを拡張して、下のように、自前の例外を作成することも可能です。

public class HeroExceptiion extends Exception {
    public HeroExceptiion(String message) {
        super(message);
    }
}

結局のところは、例外クラスの名前が変わるだけなのですが、この例外が出るところは決まってくるので。デバック時も問題になりません。

結構便利です。

今回はこんなところで。。。

でわでわ。。。

継承に関するページ一覧

  1. Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
  2. UMLの書き方(読み方)〜概要とクラス図〜
  3. Java クラスの継承を理解する
  4. クラスの継承〜アクセス修飾子〜
  5. クラスの継承関係を作る1
  6. クラスの継承関係を作る2

環境構築関連ページ一覧

設計関連ページ一覧

  1. 設計を始める〜1.アプリイメージ〜
  2. 設計を始める〜2.機能イメージ〜
  3. 設計を始める〜3.機能概要〜
  4. Java はじめて16 〜クラス設計から実装〜

PHP関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

JS関連ページ

  1. JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
  2. 吹き出しにYoubetubeを埋め込む
  3. Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
  4. JS XmlHttpRequest 〜JSでの同期/非同期通信〜
  5. JS Google Maps API 〜GeoLocation 現在位置の取得〜
  6. AngularJS + PHP 〜AngularJSの実装〜
  7. AngularJS + PHP 〜AngularJSの実装2〜
  8. WordPress プラグイン作成 〜$wpdbでのSELECT〜
  9. WordPressプラグイン作成 〜HTML挿入まで完了〜
  10. WordPress プラグイン作成 〜アンケート挿入〜
  11. MAMP 起動設定 〜WordPressのテスト環境を作る〜
  12. MAMP WordPress 〜インポート時のエラー対処〜
  13. WordPress PHPカスタム〜根本的に見た目を変える2〜

数理モデル関連ページ

  1. 数学への挑戦 第二弾〜数理モデルxプログラミング〜
  2. 数学への挑戦 第二弾〜実装編:数理モデルxプログラミング〜
  3. 数学への挑戦 第二弾〜集合を使う:数理モデルxプログラミング〜
  4. 数学への挑戦 第二弾〜確率変数:数理モデルxプログラミング〜
  5. 数学への挑戦 第二弾〜期待値と分散:数理モデルxプログラミング〜
  6. 数学への挑戦 第二弾〜卒業までに彼氏ができる確率:数理モデルxプログラミング〜
  7. 数学への挑戦 第二弾〜確率変数の足し算:数理モデルxプログラミング〜
  8. 数学への挑戦 第二弾〜まとめ1:数理モデルxプログラミング〜

Java ポリモーフィズム(多様性) 〜クラス型の変換と組み合わせ〜

インロトダクション

前回は抽象クラスとインターフェースについて学習しました。

今回は、以下の3つを組み合わせて、どのような仕組みを作ることができるのか?について学習したいと思います。

はっきり言って、「どのような仕組みを作ることができるのか?」の問いに対する答えは、「ほぼ無限の組み合わせ方、仕組みを作ることができる」と思います。という答えが言えます。

前回までに、クラスとクラスを組み合わせるための土台になる部分を学習しました。以下のような内容です。

  • クラスの継承関係を作成する(親クラスと子クラス)
  • 抽象クラスとインターフェース

これらの仕組み(継承関係を作る)を使用して、どのような組み合わせ方ができるのか学習していきます。
世間巷には「デザインパターン」という名前で広く知られています。が、デザインパターンなどのように広く知られるものは
大体Java APIの方で実装してあるので、使い方さえ分かっていれば改めて実装する必要はありません。

ポイントになるのは、「クラス関係を作成して、どのような仕組みを実現するか?」というところです。

ポリモーフィズムの実装

「テキストRPGを作る」ということをテーマ(話題)として話を進めていきます。

前回までに作成したもの

親クラス、子クラス。。。として以下のようなクラスを作成したいと考えています。下のようなクラスたちです。

具体例A
テキストRPGゲームを作成しようとしているので下のようなクラスを作成したいと思っている。

  1. 勇者・クラス <作成済み>
  2. 魔法使い・クラス <作成済み>
    3.盗賊・クラス
  3. 超勇者クラス
  4. マタンゴ・クラス
  5. ポインズンマタンゴ・クラス
  6. ゴブリン・クラス
  7. ウェアウルフ・クラス
  8. デスバット・クラス

そして、プレーヤー側のクラスの親になるCharacterクラスも作成しました。(詳細は前回を参照ください。)

ポリモーフィズムの設計

先ずは、設計から入ります。

組み合わせて、プログラムを実行するのに「どう組み合わせるか?」を考えないと始まらないのです。

自分の場合は、まずは紙と鉛筆を持ってプログラミングを始めます。つまり「設計から始める」ということです。

RPGのパーティを考える

例えば、前回作成したCharacterクラスとは別に「RpgCharacter」というクラスを作成します。
このクラスは、下の図のように勇者クラス(RpgHero)や魔法使いクラスを作成したとします。

そうすると、下のようなコードが書けます。(エラーが出ません)

<メインメソッド>

    public static void main(String[] args) {
        RpgCharacter[] party = new RpgCharacter[2];
        party[0] = new RpgHero("太郎");
        party[1] = new RpgWizard("二郎");

        System.out.println("こんにちは、良いパーティですね。");
        for (int i = 0; i < party.length; i++) {
            System.out.println(party[i].getName() + "さん");
        }
    }

このように、RpgCharacterクラスを継承しているクラスなので、RpgCharacterの配列をパーティとして扱うことができます。

そして、MonsterもRpgCharacterを継承することになるので、モンスターも仲間(パーティ)に追加できます。

RpgCharacterに処理をまとめる

現状では、勇者や魔法使い、モンスターなどの使用できるコマンドは「たたかう」というコマンドのみです。

ここに、すべてのキャラクターに共通する「にげる」コマンドを追加します。
それには、下のようにコードを追加します。

<RpgCharacterクラスの修正>

public abstract class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;

    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }
}

この状態で、RpgCharacterクラスを継承するクラスは「にげる」コマンドを使用することができます。

しかし「全く同じではない処理を追加する場合」はどうでしょうか?

例えば、「たたかう」コマンドの場合です。勇者クラスと魔法使いクラスでは、攻撃力が違うはずです。

この場合は、与えるダメージの大きさをそのクラスによって変えられるようにする必要があります。

その仕組みとして「抽象メソッド」を使用する方法があります。

具体的には下のようなコードです。

<抽象メソッドを追加する>

    /** たたかう */
    public abstract void attack(RpgCharacter character);

実装したクラスのコードは下のようになります。

<抽象メソッドを追加したクラス>

public abstract class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;

/** たたかう */
    public abstract void attack(RpgCharacter character);
    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }

そして、これらのクラスを生成するとき(newするとき) には、コンストラクタが必要になりますが、これもRpgCharacterクラスと同じものになりますので、下のようにコンストラクタを追加します。

<コンストラクタを追加したクラス>

public abstract class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;

    /** コンストラクタ */
    public RpgCharacter(String name) {
        this.name = name;
        this.hp = 10;
        this.mp = 0;
    }

/** たたかう */
    public abstract void attack(RpgCharacter character);
    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }

そうすると初めにも書きましたが、このようなコードでの処理が実行できます。

    public static void main(String[] args) {
        RpgCharacter[] party = new RpgCharacter[2];
        party[0] = new RpgHero("太郎");
        party[1] = new RpgWizard("二郎");

        System.out.println("こんにちは、良いパーティですね。");
        for (int i = 0; i < party.length; i++) {
            System.out.println(party[i].getName() + "さん");
        }
    }

実行結果は下のようになります。

こんにちは、良いパーティですね。
太郎さん
二郎さん

<<<前回 次回 >>>

継承に関するページ一覧

  1. Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
  2. UMLの書き方(読み方)〜概要とクラス図〜
  3. Java クラスの継承を理解する
  4. クラスの継承〜アクセス修飾子〜
  5. クラスの継承関係を作る1
  6. クラスの継承関係を作る2

環境構築関連ページ一覧

設計関連ページ一覧

  1. 設計を始める〜1.アプリイメージ〜
  2. 設計を始める〜2.機能イメージ〜
  3. 設計を始める〜3.機能概要〜
  4. Java はじめて16 〜クラス設計から実装〜

PHP関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

JS関連ページ

  1. JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
  2. 吹き出しにYoubetubeを埋め込む
  3. Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
  4. JS XmlHttpRequest 〜JSでの同期/非同期通信〜
  5. JS Google Maps API 〜GeoLocation 現在位置の取得〜
  6. AngularJS + PHP 〜AngularJSの実装〜
  7. AngularJS + PHP 〜AngularJSの実装2〜
  8. WordPress プラグイン作成 〜$wpdbでのSELECT〜
  9. WordPressプラグイン作成 〜HTML挿入まで完了〜
  10. WordPress プラグイン作成 〜アンケート挿入〜
  11. MAMP 起動設定 〜WordPressのテスト環境を作る〜
  12. MAMP WordPress 〜インポート時のエラー対処〜
  13. WordPress PHPカスタム〜根本的に見た目を変える2〜

数理モデル関連ページ

  1. 数学への挑戦 第二弾〜数理モデルxプログラミング〜
  2. 数学への挑戦 第二弾〜実装編:数理モデルxプログラミング〜
  3. 数学への挑戦 第二弾〜集合を使う:数理モデルxプログラミング〜
  4. 数学への挑戦 第二弾〜確率変数:数理モデルxプログラミング〜
  5. 数学への挑戦 第二弾〜期待値と分散:数理モデルxプログラミング〜
  6. 数学への挑戦 第二弾〜卒業までに彼氏ができる確率:数理モデルxプログラミング〜
  7. 数学への挑戦 第二弾〜確率変数の足し算:数理モデルxプログラミング〜
  8. 数学への挑戦 第二弾〜まとめ1:数理モデルxプログラミング〜

Java 未来に備えるための継承 〜親クラスの作成とインターフェース〜

イントロダクション

前回は、継承に関して学習しました。

継承関係を作る目的は大まかにしたの2つがあります。

  1. クラスのグループ化
  2. クラスの拡張

そして、クラスをグルーピング、拡張することで以下のようなメリットがあります。

  • 親クラスのメソッドも使用できる
  • 親クラスと似て非なるメソッドができる

この特性を使用して、実際に使用されているのがJava APIにあります。

<画面コンポーネント系のクラス(java.net)>

<インターフェースのケース>

  • java.util.List
    ArrayList, LinkedListなどのクラスをグルーピングしている
  • java.util.Set
    HashSet, EnumSet名どのクラスをグルーピング
  • java.util.Map
    HashMap, LinkedMapなどのクラスをグルーピング

未来に備える実装の例

具体例A
テキストRPGゲームを作成しようとしているので下のようなクラスを作成したいと思っている。

  1. 勇者・クラス
  2. 魔法使い・クラス
    3.盗賊・クラス
  3. 超勇者クラス
  4. マタンゴ・クラス
  5. ポインズンマタンゴ・クラス
  6. ゴブリン・クラス
  7. ウェアウルフ・クラス
  8. デスバット・クラス

これらのクラスを作るのに、全てのクラスに「HP」「名前」が必要になりますが、毎回同じコードを書くのは
スマートではありませんので「継承(extends)」を行うことでこれらを解消することができました。
※↑前回の学習内容

そして、インターフェースも同様に「同じカテゴリ」のクラスを作ることができます。

具体的には、自分が作成した「CommandIF」のことです。

<=========今回の学習ポイント=====================>

  1. 今回は、通常のクラスにある、メソッドとは別の「抽象メソッド」に関して学びます。
  2. 通常のクラス + 「抽象メソッド」= 【抽象クラス】
  3. 「抽象メソッド」と定数のみ = 【インターフェース】

<=======================================>

未来に備えるための継承

プログラミングを行う時には、大まかに2種類あります。

  1. 目的のものを実装する
  2. 今後あると便利なものを実装する

※普段から今後使えるものを作ろうとするのは基本ですが。。。

先ほどの具体例Aを例にすると、下のようなクラス群を作成するのに共通するプロパティ(属性)を持ったクラスを継承して作成するのが、良さそうです。※この時点では、アイディアのレベルで良いのです。

  1. 勇者・クラス
  2. 魔法使い・クラス
    3.盗賊・クラス
  3. 超勇者クラス
  4. マタンゴ・クラス
  5. ポインズンマタンゴ・クラス
  6. ゴブリン・クラス
  7. ウェアウルフ・クラス
  8. デスバット・クラス

なので、これらのクラスに共通するプロパティ(属性)を考えます。
UMLを使用すると、直感的に考えることができます。

とりあえずは。このような形で、クラスの継承関係を作成すると「HP」と「名前」はCharacterクラスのみに作成すれば問題なくできそうです。

2つの不都合

ここで不都合が生じます。具体的には「詳細不明のメソッド」が出てくるというところです。

具体的問題は、

「勇者クラスと魔法使いクラスの攻撃力、ダメージは同じでしょうか?」

答えはNOだと思います。そんな魔法使いがいても良いのかもしれませんが。。。

しかし、勇者と魔法使いが同じ攻撃を行うのでは、職種が違う意味がありません。

なので、「戦う」というメソッドの処理内容をそれぞれのクラスで別の処理にしてやる必要があります。

ただし、メソッドの名前は同じである必要があります。

なので、HeroやWizardなどのプレーヤー側のクラスの親になるクラス「Character」を作成してやれば、コードとしては下のような形になります。共通するプロパティ(属性)は全てCharacterクラスで管理するようにします。

<Heroクラス>

package yoshida.tkm.lesson10;

/**
 *
 * @author 作成者の名前
 * 2021/05/19
 */
public class Hero extends Character {
    /** 定数 */
    final String TEISU = "定数";

    /** コンストラクタ */
    public Hero() {
        // newしたときに呼び出される
        System.out.println("*** Hero コンストラクタ ***");
    }

    /** 戦う */
    public void attack(Matango m) {
        System.out.println(getName() + "の攻撃");
        m.hp -= 5;
        System.out.println("5ポイントのダメージをあたえた!");
    }

    /** 逃げる */
    public void run() {
        // コメント
        System.out.println(getName() + "は逃げ出した!");
    }
}

CharacterクラスにHPと名前が定義してあるので、これらのフィールド変数は親クラスのものを使用します。

そして、Heroクラスには「戦う」と「逃げる」、のコマンドがあります。

<Wizardクラス>

public class Wizard extends Character {
    public void heal(Hero hero) {
        hero.setHp(hero.getHp() + 10);
        System.out.println(hero.getName() + "のHPを10ポイント回復した");
    }
 }

Wizardクラスは回復の魔法が使えます。そして、HP、名前は親クラスのものを使用します。

ここで問題なのは。。。

勇者クラスでなくとも「戦う」コマンドは使用できなくてはおかしい、というところです。
なので、上記のWizardクラスにattack()メソッドを追加します。

<Wizardクラス②>

public class Wizard extends Character {
    /** 戦う */
    public void attack(Matango m) {
        System.out.println(getName() + "の攻撃");
        m.hp -= 5;
        System.out.println("5ポイントのダメージをあたえた!");
    }

public void heal(Hero hero) {
        hero.setHp(hero.getHp() + 10);
        System.out.println(hero.getName() + "のHPを10ポイント回復した");
    }
 }

この状態では、勇者クラスの「戦う」と同じ処理になりますが、魔法使いは別な「戦う」方法があるはずです。

この部分の解決方法としては抽象メソッドを使用するという方法があります。

具体的には、Characterクラスに抽象メソッドをもたせてやるという方法です。

しかし、「抽象メソッドをもたせると、通常のクラスではなく抽象クラスとして定義する」必要があります。

通常のクラスと違い下のように定義します。

「書き方」
public abstract class クラス名{
// 通常クラスと変わらないが抽象メソッドを作る必要がある
public abstract 返却値 メソッド名(引数);
}

<Characterクラス>

public abstract class Character {
    // java.lang.Character;
    private String name;
    private int hp;
    private int mp;

    /** 抽象メソッド */
    public abstract void attack(Matango m);

/**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return the hp
     */
    public int getHp() {
        return hp;
    }
    /**
     * @param hp the hp to set
     */
    public void setHp(int hp) {
        this.hp = hp;
    }
    /**
     * @return the mp
     */
    public int getMp() {
        return mp;
    }
    /**
     * @param mp the mp to set
     */
    public void setMp(int mp) {
        this.mp = mp;
    }
}

これで、HeoクラスとWizardクラスは同じ名前で全く別の処理**のメソッドを作成することができます。
さらに、メソッドの
オーバーライド**を強制させることができるので、オーバーライドのし忘れを防止することができます。

public class Wizard extends Character {
    /** 戦う */
    @Override
    public void attack(Matango m) {
        System.out.println(getName() + "の攻撃");
        m.hp -= 2;
        System.out.println("2ポイントのダメージをあたえた!");
    }

<<<前回 次回>>>

継承に関するページ一覧

  1. Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
  2. UMLの書き方(読み方)〜概要とクラス図〜
  3. Java クラスの継承を理解する
  4. クラスの継承〜アクセス修飾子〜
  5. クラスの継承関係を作る1
  6. クラスの継承関係を作る2

環境構築関連ページ一覧

設計関連ページ一覧

  1. 設計を始める〜1.アプリイメージ〜
  2. 設計を始める〜2.機能イメージ〜
  3. 設計を始める〜3.機能概要〜
  4. Java はじめて16 〜クラス設計から実装〜

PHP関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

JS関連ページ

  1. JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
  2. 吹き出しにYoubetubeを埋め込む
  3. Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
  4. JS XmlHttpRequest 〜JSでの同期/非同期通信〜
  5. JS Google Maps API 〜GeoLocation 現在位置の取得〜
  6. AngularJS + PHP 〜AngularJSの実装〜
  7. AngularJS + PHP 〜AngularJSの実装2〜
  8. WordPress プラグイン作成 〜$wpdbでのSELECT〜
  9. WordPressプラグイン作成 〜HTML挿入まで完了〜
  10. WordPress プラグイン作成 〜アンケート挿入〜
  11. MAMP 起動設定 〜WordPressのテスト環境を作る〜
  12. MAMP WordPress 〜インポート時のエラー対処〜
  13. WordPress PHPカスタム〜根本的に見た目を変える2〜

数理モデル関連ページ

  1. 数学への挑戦 第二弾〜数理モデルxプログラミング〜
  2. 数学への挑戦 第二弾〜実装編:数理モデルxプログラミング〜
  3. 数学への挑戦 第二弾〜集合を使う:数理モデルxプログラミング〜
  4. 数学への挑戦 第二弾〜確率変数:数理モデルxプログラミング〜
  5. 数学への挑戦 第二弾〜期待値と分散:数理モデルxプログラミング〜
  6. 数学への挑戦 第二弾〜卒業までに彼氏ができる確率:数理モデルxプログラミング〜
  7. 数学への挑戦 第二弾〜確率変数の足し算:数理モデルxプログラミング〜
  8. 数学への挑戦 第二弾〜まとめ1:数理モデルxプログラミング〜

Java クラスの継承を理解する

イントロダクション

Javaなどのオブジェクト指向言語では、ほとんどの言語がクラスの継承を行うことができます。なので、継承するとどのようなメリットがあるのか、それとも意味のないことなのか?ここを判断してクラス設計を行えるようになるための考え方を伝えられれば良いと思っています。

継承を行うメリットに関して、お話する前に予備知識として次の部分を理解しておいて干飯です。

前提1:データ型、演算子などを理解している

データ型などの説明をしている記事の一覧、検索結果です

前提2:Javaの基本文法の理解をしている

これも基本文法の記事を検索した結果のリンクです。

継承のついて

今回は、クラスの継承関係を作成しその関係について記載しました。
継承関係を作って実行してみました。 => 親、兄、弟のクラスを作成して実行した動画です。

クラスの継承

クラスの継承を行う目的は大まかに2つあります。

  1. 作成するクラスをグループ化したい
  2. 親クラスを拡張したい

上記のような目的が多いです。

==クラスの継承方法==
<親クラスの例>

package jp.zenryoku.practice.train2.cls.familly;

public class Parent extends Gosenzo {
/** 家計 */
public static int kakei = 1000;
/** 苗字 */
protected String lastName;
/** 名前 */
private String name;
/** 年齢 */
private int age;
/** 趣味・特技 */
public String favorit;

/** コンストラクタ */
public Parent() {
lastName = "tanaka";
name = "takao";
age = 50;
favorit = "ケツで箸を割る";
}

/** Gosenzoクラスの抽象メソッド */
@Override
public String kekkeiGenkai() {
return "白目をむく";
}

/** 自己紹介 */
public void say() {
System.out.println(lastName + "と申します。");
System.out.println("親です。名前は" + name + "です。年齢は" + age + "です。");
System.out.println("特技は、「" + favorit + "」です。");
}

/** おかしな部分 */
protected void funny() {
System.out.println("喜びを真逆の言葉で表現する。");
}

/** 買い物 */
public void buy(int money) {
kakei -= money;
System.out.println("残金:" + kakei);
}
}

<子クラスの例>

package jp.zenryoku.practice.train2.cls.familly;

import java.util.List;

import jp.zenryoku.practice.train2.cls.game.util.CommandData;
import jp.zenryoku.practice.train2.cls.game.util.CommandIF;

public class ChildAni extends Parent implements CommandIF {
/** 年齢 */
private int age;
/** 名前 */
private String name;

public ChildAni() {
name = "taro";
age = 12;
createCommandList();
}

@Override
public void say() {
System.out.println(lastName + "です。");
System.out.println("兄です。名前は" + name + "です。年齢は" + age + "です。");
System.out.println("特技は、「" + favorit + "」です。");
}

@Override
public List<CommandData> getCommandList() {
addCommand(createCommandData("すかしっぺ", 15));
addCommand(createCommandData("漢の一撃", 20));
return super.getCommandList();
}

/**
* コマンド実行する
* @param index
*/
public int exeCommand(String index) {
CommandData data = super.getCommandList().get(Integer.parseInt(index));
System.out.println(this.name + "の" + data.getCommandName());
return data.getCommandValue();
}
}

このようなクラスを題材に話を進めていきたいと思います。

クラスをグループ化する

クラスをグループ化する場合、上記の「親クラス」と「子クラス」の場合は、人間の親子を表しています。

なので、Parentクラスのグループ(親クラス)に対して子クラスは、兄とか弟ができるというところです。

しかし、このグループに車とか、犬などの子クラス(Parentクラスを継承したクラス)を作ると「役割」がまとまらなくなってしまいます。

なので、同じカテゴリのもの(クラス)**を作成していきましょう。

実際に、このグループに属するクラス「弟」クラスを作成すると下のようになります。

package jp.zenryoku.practice.train2.cls.familly;

public class ChildOtoto extends Parent {
/** 年齢 */
private int age;
/** 名前 */
private String name;

public ChildOtoto() {
name = "jiro";
age = 10;
favorit = "鼻を膨らます";
}

@Override
public void say() {
System.out.println(lastName + "といいます。");
System.out.println("弟です。名前は" + name + "です。年齢は" + age + "です。");
System.out.println("特技は、「" + favorit + "」です。");
}

@Override
public void funny() {
super.funny();
}
}

このクラス以外にも、姉、妹、孫など作成することが可能です。※犬なども作れますが、混乱の元なのでやめましょう。

クラスを拡張する

そして、「クラスを継承する目的」その2に関してです。

Parentクラスは以下の属性(フィールド変数)を持っています。

    /** 家計 */
public static int kakei = 1000;
/** 苗字 */
protected String lastName;
/** 名前 */
private String name;
/** 年齢 */
private int age;
/** 趣味・特技 */
public String favorit;

なので、このParentクラスは「家系」を管理しているクラスというとこができます。

そして、このクラスを継承したクラスChildAniは親クラスの「buy()」メソッドを使用することができます。

つまり、親クラスにあるkakeiを参照することができます。アクセス修飾子が「public」だからというのもありますが。。。

結局のところは、親クラスのメソッドを使用することができるというところです。

ちょっと待て〜い

「拡張」する話はどこに行ったのか?と疑問に思う方もいると思います。

まずは、上記の「親クラスのメソッドも使用できる」というところが大切なのです。

この親クラスのメソッドを使用できるというところで、子クラスでは同じメソッドを定義した場合どのようになるのでしょうか?

オーバーライド

このようになります。「オーバーライドって何?」と思った方、通常でございます。

この「オーバーライド」こそが「拡張」する部分になります。

元々あったメソッド「親クラスのbuy()」メソッドは子クラスからでも下のような書き方で呼び出すことが可能です。

super.buy();

逆に下のように書いた場合

this.buy();

これは、子クラスに書いたbuy()メソッドを呼び出すことになります。

具体的には下のようなコードになります。
<親クラスを拡張した兄クラス>

public class ChildAni extends Parent implements CommandIF {
  ・
  ・※省略
  ・
/** 買い物 */
public void buy(int money) {
// 親クラスの処理から値引き処理( - nebiki)が追加されている
int nebiki = 100;
kakei -= (money - nebiki);
System.out.println("残金:" + kakei);
}
}

親クラスでの「buy(買い物)」メソッドでは引数の値を「kakei(家計)」から直接、減算していましたが、兄クラスでは「値引き」が入ります。

このように、親クラスのbuy()メソッドを使うよりも、兄クラスのbuy()メソッドを使用した方が安く済むということです。

これを現実に例えていうならば、親よりも、兄の方が買い物上手といったところでしょうか?

次回

継承に関するページ一覧

  1. Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
  2. UMLの書き方(読み方)〜概要とクラス図〜
  3. Java クラスの継承を理解する
  4. クラスの継承〜アクセス修飾子〜
  5. クラスの継承関係を作る1
  6. クラスの継承関係を作る2

環境構築関連ページ一覧

設計関連ページ一覧

  1. 設計を始める〜1.アプリイメージ〜
  2. 設計を始める〜2.機能イメージ〜
  3. 設計を始める〜3.機能概要〜
  4. Java はじめて16 〜クラス設計から実装〜

PHP関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

JS関連ページ

  1. JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
  2. 吹き出しにYoubetubeを埋め込む
  3. Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
  4. JS XmlHttpRequest 〜JSでの同期/非同期通信〜
  5. JS Google Maps API 〜GeoLocation 現在位置の取得〜
  6. AngularJS + PHP 〜AngularJSの実装〜
  7. AngularJS + PHP 〜AngularJSの実装2〜
  8. WordPress プラグイン作成 〜$wpdbでのSELECT〜
  9. WordPressプラグイン作成 〜HTML挿入まで完了〜
  10. WordPress プラグイン作成 〜アンケート挿入〜
  11. MAMP 起動設定 〜WordPressのテスト環境を作る〜
  12. MAMP WordPress 〜インポート時のエラー対処〜
  13. WordPress PHPカスタム〜根本的に見た目を変える2〜

数理モデル関連ページ

  1. 数学への挑戦 第二弾〜数理モデルxプログラミング〜
  2. 数学への挑戦 第二弾〜実装編:数理モデルxプログラミング〜
  3. 数学への挑戦 第二弾〜集合を使う:数理モデルxプログラミング〜
  4. 数学への挑戦 第二弾〜確率変数:数理モデルxプログラミング〜
  5. 数学への挑戦 第二弾〜期待値と分散:数理モデルxプログラミング〜
  6. 数学への挑戦 第二弾〜卒業までに彼氏ができる確率:数理モデルxプログラミング〜
  7. 数学への挑戦 第二弾〜確率変数の足し算:数理モデルxプログラミング〜
  8. 数学への挑戦 第二弾〜まとめ1:数理モデルxプログラミング〜

Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~

イントロダクション

※筆者が昔、オラクルの教科書(JavaSEの資格用教科書)とか、ウェブページを見ていて見つけたものを記載しています。
 出典元を忘れてしまいました。。。

オブジェクト指向という言葉は皆さん聞いたことがあると思います、これについてちゃんと理解しようと思います。

オブジェクト指向コンセプト

1.なぜオブジェクト指向か

システム開発における課題」 -> 開発期間の短縮
顧客の要求やサービスは、早いサイクルで変化します。そのためシステム開発に多くの時間を
かける余裕がありません。それどころか、開発が進んでいる最中に、要求やサービスの内容が
変わっても期間内の対応が求められます。

つまり、仕様変更があっても開発期間が変わらないので、多くの開発者が会社にお泊りすることが
多々ありました。

システムの仕様変更に伴うコストの削減

前述の通り、開発途中で顧客の要求する内容が変わることもありますが、そのたびに初めから
作成していては、期間が延びるだけでなく、費用がかさみます。顧客は最小コストで最大限の
成果を期待します。

この様な問題点に対応するためには、システム開発において、次のことを実現するべきです。

1.1.3実現するべきこと

  1. 以前に作成したプログラムを再利用する
  2. 大勢のエンジニアで共同開発を行う
  3. プログラムの変更箇所をいち早く特定し対応する
  4. あるプログラムの変更がほかのプログラムに影響しないようにする

もし、巨大な1本のプログラムとして構築したら、これらを実現するのは、困難です。しかし、システムを管理しやすい単位で分割し、それらを組み合わせる形式で構築すれば、これらを実現することができます。

この分割の単位をオブジェクトとするのが、オブジェクト指向による開発です。

2. オブジェクトとは

それでは、システム分割単位である「オブジェクト」とは、いったいどのようなものでしょうか。
ここでは、「エアコン」を制御するプログラムを例にそれを見ていきます。

下の図ではエアコンが持つ情報や機能を書きだしたものです。「電源」は電源の情報(状態)、「設定温度」や「運転モード」は使う人が操作した情報(状態)を示すものです。
一方、「電源をON(あるいはOFF)にする」「運転モードを切り替える」「設定温度を変更する」などは、エアコンを操作するための機能です。

エアコンを例にすると。。。

<エアコンの場合>

表1<エアコンの制御プログラム>

係(役目) 情報(状態) 機能
電源係 電源 電源をON、OFFにする
温度係  設定温度 設定温度の変更
温度係  現在の室温 現在の室温を測る
運転係  運転モード 運転モードを切り替える
運転係  運転モード 温度差によって動作を変える

エアコンを制御するプログラムは、こうした情報(状態)を保持し、参照しつつ使う人の操作に応じてエアコンをどうさせます。

<ポイント>

情報(状態)と機能は密接にむずびついて1つの係・役割を担い、それ以外の情報(状態), 機能から互いに独立しているということです。
そして、エアコンが操作されるとそれぞれの係は、互いに要求を行い、連携して動作します。

運転モードの着替え時の場合

運転モードを「冷房」から「ドライ」に変更する操作が行われたら、「運転係」から「温度係」へ設定温度の変更をする「要求」が行われます。また、「運転係」は現在の室温と設定温度を確認しないと動作(冷やす、温める)を決められないので、「温度係」にそれらの情報(状態)を問い合わせます。

この場合、「係」に相当するのが、システムにおけるオブジェクトです
また、「温度係」が電源のON/OFFを切り替えたり、「運転係」が設定温度を変更したりしません。
それぞれの係は自分のすべきことが明確に分かれています(独立性)。

子の係の独立性のおかげで、オブジェクト指向による開発では、作業もオブジェクトごとに独立して進めることができます。開発を終えたオブジェクトは、他のオブジェクトから簡単に利用することができます。また、おかしな動作を押する箇所が出ても、オブジェクト単位で調査・修正するので、利用しているオブジェクトへの影響を軽減することができます。
これにより上記の実現するべき点をかなえることができます。

オブジェクト指向言語の機能

属性と操作

Java言語を使用した場合、オブジェクトをどのように表現するのか見ていきましょう。ここでもエアコンの制御プログラムを例に確認します。

先ほどの表(下の図)を見ながら読み進めてください。


表1<エアコンの制御プログラム>

係(役目) 情報(状態) 機能
電源係 電源 電源をON、OFFにする
温度係  設定温度 設定温度の変更
温度係  現在の室温 現在の室温を測る
運転係  運転モード 運転モードを切り替える
運転係  運転モード 温度差によって動作を変える

前節では、係がオブジェクトに相当することを説明しました。さらにオブジェクト指向では、上の図
にある情報(状態)のことを属性と呼び機能のことを操作と呼びます。
例えば、電源系(電源オブジェクト)は「電源」という属性と「電源をONにする」「電源をOFFにする」
という操作が一つのセットにな手出来ています。
温度係(温度オブジェクト)も運転係(運転オブジェクト)も同様です。

つまり、オブジェクトは属性と操作を一体化することで表現されます。

属性と操作

属性と操作について詳しく見ていきましょう。属性は変数として、表現され、名前と値を持ちます。

  • 属性「室内温度」⇒値「30度」
  • 属性「設定温度」⇒値「27度」

属性をプログラムで表現する場合の例

/** 温度係 */
public class Temparature {
  /** 室内温度 */
  int roomTemparature;
  /** 設定温度 */
  int settingTemparature;

 /** コンストラクタ */
  public Temparature() {
    // 例なのでコンストラクタで値を設定する
    roomTemparature = 30;
    settingTemparature = 27;
  }  
}

操作は、他のオブジェクトや自分自身(自オブジェクト)から呼び出されることにより動作し、
そのオブジェクトの状態を変えたり、さらにそこからほかのオブジェクトの操作を呼び出したり
できます。下の図Aでは、温度オブジェクトに「設定温度を変更する」という操作(メソッド)
が定義されています。エアコンの設定温度を変更したい場合には、属性へ直接アクセスするのではなく
この操作を呼び出します。
図A

属性と操作をプログラムで表現する場合の例

/** 温度係 */
public class Temparature {
  /** 室内温度 */
  int roomTemparature;
  /** 設定温度 */
  int settingTemparature;

 /** コンストラクタ */
  public Temparature() {
    // 例なのでコンストラクタで値を設定する
    roomTemparature = 30;
    settingTemparature = 27;
  }

  /** 設定温度を変更する */
  public void setSettingTemparature(int setTamparature) {
    this.setTamparature = setTamparature;
  }
}

カプセル化とデータ隠蔽

オブジェクト指向では、そのオブジェクトが持つ属性と操作を一体化して表現すると説明しましたが、
これをカプセル化と呼びます。

カプセル化には次のようなメリットがあります。

  1. オブジェクトの内部構造を知る必要がない
  2. 属性値の変更は、操作経由に制限できる
  3. 操作名が同じなら、内部構造が変わっても利用する側にそれを意識させない
  4. 属性に不整合な値が入らないよう、操作でチェックできる

エアコンの温度オブジェクトで、カプセル化のメリットを見ていきましょう。

エアコンの設定温度を変更する場合、リモコンなどから、エアコンの「設定温度を変更する」機能
を呼び出します。この時、エアコンの内部構造や制御プログラムなどを知る必要はありません(1)。

そして、それらに直接触れることもありません。ただリモコンで操作するだけです(2)。

もし、後継機種などで「設定温度を変更する」機能の仕組みが変わっても、操作方法が同じなら
そのことで困ることはないでしょう(3)。

さらに温度設定を100度にしたり、零下40度したりはできません。「設定温度を変更する」が、
そのような設定を受け入れないからです(4)。

また、2, 4により、属性を外部から保護することをデータの隠蔽と呼びます。

図B

クラスと継承

クラス

今度は、電源オブジェクトについてみていきます。
前述では、伝げのぶじぇくとの情報(状態)として「電源」、機能として「電源をONにする」と
「電源をOFFにする」を取り上げました。しかし、エアコンの種類によっては、電源をつけると
電気代を表示する機能や、フィルタの汚れ具合を確認し掃除ランプを表示する機能がついている
ものもあります。
これらすべてを電源オブジェクトとして表現したとき、「共通している属性や機能」と
「独自に持っている属性や機能」とに分けられます。

まず、この共通している部分に着目し、土台となるひな型を作成します。
この作業を抽象化と呼びます。また抽象化した結果、オブジェクトを作成するための土台となる

ひな型をクラスと呼びます。

下の図は電源オブジェクト(電気代), 電源オブジェクト(フィルタ)の属性と機能をそれぞれ洗い出した
結果、共通項目が見つかった様子を表しています。

図C

こうしたオブジェクトの共通項目を集め、定義した門が一般的な「電源」クラスとなります。

インスタンス化

クラスは、オブジェクトを作詞得するためのひな型です。つまり設計図にすぎません。
「電源」の設計図ができても、私たちが使える「モノ」ではないのです。
子の設計図であるクラスをもとにして、実際に使うことができる「モノ」にするにすることを
インスタンス化と呼びます。

例えば、下の図Dのようにエアコンを表現するためには、電源クラス、温度クラス、運転クラスが
必要です。それらをまとめるクラスとして、エアコンクラスが必要です。各クラスをもとにインスタンス化することで、それぞれオブジェクト(実際に使うことができる「モノ」)が作成され、実際のエアコンを表現することができます。

また、クラスをもとに複数のインスタンス化を行えば、複数のオブジェクトを作成することができます。
ふたつのエアコンオブジェクトが作成されれば、エアコンオブジェクトを二つ動かすことができます。

これらのオブジェクトは同じクラスから作成されているため、同じ属性・操作を持ちます、しかしエアコンオブジェクトは別々に動きます。

具体的には、上のエアコンオブジェクト二つをエアコンAとエアコンBとしたとき、Aの電源をONにしてBの電源をOFF似しておくこともできます。もちろんエアコンAは設定温度20度、エアコンBは設定温度30度として二つとも動かすことができます。

継承

先ほど説明した電源オブジェクトは、電気代を計算したり、ランプを表示する機能がついていません。
そして、この二つの機能を付けた電源オブジェクトを作成するときに定義する電源クラスを作成する
ためには、新たに電源クラスを作成しなくてもよいのです。

つまり、一度作成した電源クラスを拡張して新しいクラスを定義することができます。
これを継承と呼びます。

オブジェ駆使項では、継承の元となるクラスをスーパークラス(親クラス)と呼び、
拡張したクラスのことをサブクラス(子クラス)と呼びます

継承により定義されたサブクラスは、スーパークラスの属性、操作を受け継ぎます。
そのため、サブクラスでは独自に持つ属性、操作のみを定義するだけで済みます。
サブクラスをインスタンス化すれば、スーパークラスの属性・操作を引き継いだオブジェクトを
が生成されます。

下の図Cのように、共通項目を洗い出し、新たに拡張したクラスを作成する手段として継承
あります。

図C

具体的には下のように実装します。

<電源クラス>

/** 電源クラス */
public class PowerSupply {
  /** 電源 */
  private boolean power;

  /** 電源ON */
  public void switchOn() {
    this.power = true;
  }  

  /** 電源OFF */
  public void switchOn() {
    this.power = false;
  }    
}

<電源クラス(電気代)>

/** 電源クラス(電気代) */
public class ElectricBillPowerSupply {
  /** 電気代 */
  private int electricBill;

  /** 電気代計算 */
  public void calcBill() {
    // 電気代計算の処理
  }
  /** 電気代表示 */
  public void calcBill() {
    // 電気代表示の処理
  }
}

インターフェースとポリモフィズム

私たちは日常でも、たくさんのオブジェクト(モノ)を利用しています。
それらの中には、全く物は別だが、同じ操作方法で利用できるものがあります。

例えば携帯電話はメーカー各社からたくさんの製品が開発・発売されていますが、「電話をかける」
や「電話を切る」という操作はマニュアルを見なくてもできるのではないでしょうか。
また、MDコンポ、CDコンポ、MP3プレーヤーはそれぞれ内部構造も再生する媒体も異なる機械
ですが、「再生」「停止」などの操作に迷わないと思います。

この様に、オブジェクトを利用する側に公開すべき操作をまとめたクラスの仕様のことを
インターフェースと呼びます。

また、共通のインターフェースを持つ操作でも、実際にはオブジェクトごとに振る舞いや動作が異なる
ことをポリモフィズム(多様性・多相性)といいます。

同じ再生ボタンでも、MDコンポ、CDコンポ、MP3プレーヤーの内部で行われることはそれぞれ違います。
もし、MDコンポ、CDコンポ、MP3プレーヤーでインターフェースを統一させていないとしたらどうでしょう。

使用者は、音楽を聴くためのオブジェクトを変える度に違う操作をしなくてはいけません。
これでは面倒で使いづらいでしょう。

プログラムの作成でも同じことが言えます。処理内容は異なるが目的は同じという機能であれば、
オブジェクトに対して、共通のインターフェースを定義し、ポリモフィズムを実現することで、
操作方法が統一され、利便性を高めることができます。ほかのプログラムを再利用することも容易です。

また、後でプログラムを手直ししてでも、機能を呼び出す方法が変わらなければ、
それを呼び出すプログラムには、なにも影響しません。これもインターフェースのメリットの一つです。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ(Mac版)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. オブジェクト指向の概念1〜OracleDocのチュートリアル1〜
  2. オブジェクト指向の概念2〜クラスとは〜

Java Basic Class Inheritance ~クラスの継承関係を作る2~

イントロダクション

前回作成したクラスを使用して実装してきます。

作成したクラスは以下のものです。

  • Parent: 親クラス
  • ChildAni: 子クラス
  • ChildOtoto: 子クラス
  • MainFamilly: メインメソッドを持っているクラス

実行するクラスは「MainFamilly」クラスのメインメソッドです。

とりあえずは実行すると下のようなイメージで動きます。

そして、抽象クラスを追加しました。クラス名は「Gosenzo(ご先祖)」です、

感の良い方は気がついているかもしれませんが、以下のような系譜ができています。

  1. ご先祖
  2. あに、弟

ただし、抽象クラスのメソッド「血継限界」に関しては実行していません。単純に実装するのを忘れていました。
ソースに関してはこちらにあるので、ご自由に動かして見てください。※カスタムして遊んでもよし。

継承関係で遊ぶ

勝手に設定を作ってみます。

ご先祖から因縁を持っている魔物、スライム討伐が稼業になっている家族の物語、細かい話はスッ飛ばして宿敵スライムと対峙する家族であった。。。

というわけで、スライムとの戦いを始めたいと思います。

現在作成済みのクラスは上記のとおり4クラスあります。
そして、ご先祖クラスには、フィールドが「秘伝のタレ」しかありません。

なので、RPGっぽく作成するため以下のプロパティ(フィールド)を追加します。本当は「プレーヤー」クラスの方が良いのですが。。。

いや、プレーヤークラスを作成しましょう。
※GetterとSetterは省略しています。GetterSetterの作成方法は下の動画を参照ください。

<プレーヤークラス>

public class Player {
    /** 名前 */
    private String name;
    /** レベル */
    private int level;
    /** 生命力 */
    private int HP;
    /** 特殊能力(技能)の使用時に消費 */
    private int MP;
    /** 攻撃力 */
    private int attack;
    /** 防御力 */
    private int diffence;
    /** 戦闘可能フラグ */
    private boolean canBattle;

    /**
     * コンストラクタ。
     * @param name
     */
    public Player() {
        this.name = "桃太郎";
        // レベル1の設定
        setLevel(1);
        setHP(20);
        setMP(10);
    }

    /**
     * コンストラクタ。
     * @param name
     */
    public Player(String name) {
        this.name = name;
        // レベル1の設定
        setLevel(1);
        setHP(20);
        setMP(10);
    }
}

このクラスをご先祖クラスの親にします。

public abstract Gosenzo extends Player { ... }

そうすると、MainFamillyのメインメソッドで、HPなどの設定ができるようになります。
<MainFamilly>

public class MainFamilly {
    public static void main(String[] args) {
        // 標準入力の受付
        Scanner scan = new Scanner(System.in);
        System.out.println("**** Game Start ****");
        // 親クラスのインスタンス
        Parent parent = new Parent();
        // HP
        parent.setHP(50);
        //戦闘可能フラグ
        parent.setCanBattle(true);
        //攻撃力
        parent.setAttack(20);
        //防御力
        parent.setDiffence(15);

        // 子クラス(あに)のインスタンス
        ChildAni ani = new ChildAni();
        // HP
        parent.setHP(40);
        //戦闘可能フラグ
        parent.setCanBattle(true);
        //攻撃力
        parent.setAttack(20);
        //防御力
        parent.setDiffence(15);
        // 子クラス(弟)のインスタンス
        ChildOtoto ototo = new ChildOtoto();
        // HP
        parent.setHP(30);
        //戦闘可能フラグ
        parent.setCanBattle(true);
        //攻撃力
        parent.setAttack(20);
        //防御力
        parent.setDiffence(15);
        while (true) {
            String input = scan.nextLine();
            if ("parent".equals(input)) {
                parent.say();
            } else if ("ani".equals(input)) {
                ani.say();
            } else if ("ototo".equals(input)) {
                ototo.say();
            } else {
                System.out.println("処理を終了します。");
                break;
            }
        }
    }
}

モンスターを作成する

モンスターといえど、HPなどはあるので、プレーヤークラスの子クラスとして作成することにします。
そして、「話をするかどうか?」のフラグを追加することにしました。
<モンスタークラス>

public class Monster extends Player {
    /** 話をするかどうか */
    private boolean isTalk;

    /**
     * @return the isTalk
     */
    public boolean isTalk() {
        return isTalk;
    }

    /**
     * @param isTalk the isTalk to set
     */
    public void setTalk(boolean isTalk) {
        this.isTalk = isTalk;
    }
}

そして、具体的なモンスタークラスを作成します。
つまり、スライムクラスを作成します。

<スライムクラス>

public class Slime extends Monster{
    public Slime() {
        this.setCanBattle(true);
        this.setHP(10);
        this.setMP(5);
        // 攻撃力
        this.setAttack(5);
        // 防御力
        this.setDiffence(5);
    }
}

ここまで作成したら、次は戦闘シーンを作成したくなると思います。

頑張って見てください。

自分が作成したものは次回、記事に記載いたします。

<<< 前回

関連ページ一覧

Eclipse セットアップ(Mac版)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜

Java Basic Class Inheritance ~クラスの継承関係を作る1~

継承したクラスを動かす

クラスの継承関係を作成して、それを実行してクラス継承の意味と使い方を理解していきます。
その第一回目となります。
前回の記事でもアクセス修飾子について記載しています。

動きのイメージとして下のような形です。

前提

以下のクラスを作成します。

  1. Parent
  2. ChildAni
  3. ChildOtoto
  4. MainFamilly

それぞれ以下のようなコードです。
そして、MainFamilyクラスはメインメソッドを持っているクラスです。このクラスを用意することでほかのクラスを修正してもメインメソッドを修正せずに起動できるというわけです。

具体的には以下のような手順で起動確認しながら実装ができるというわけです。

  1. MainFamilly以外のクラスを修正する
  2. MainFamillyクラスのメインメソッドを起動する

Parent

public class Parent {
    /** 家計 */
    public static int kakei = 1000;
    /** 苗字 */
    protected String lastName;
    /** 名前 */
    private String name;
    /** 年齢 */
    private int age;
    /** 趣味・特技 */
    public String favorit;

    /** コンストラクタ */
    public Parent() {
        lastName = "tanaka";
        name = "takao";
        age = 50;
        favorit = "ケツで箸を割る";
    }

    /** 自己紹介 */
    public void say() {
        System.out.println(lastName + "と申します。");
        System.out.println("親です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }

    /** おかしな部分 */
    protected void funny() {
        System.out.println("喜びを真逆の言葉で表現する。");
    }

    /** 買い物 */
    public void buy(int money) {
        kakei -= money;
        System.out.println("残金:" + kakei);
    }
}

ChildAni

public class ChildAni extends Parent {
    /** 年齢 */
    private int age;
    /** 名前 */
    private String name;

    public ChildAni() {
        name = "taro";
        age = 12;
    }

    @Override
    public void say() {
        System.out.println(lastName + "です。");
        System.out.println("兄です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }
}

ChildOtoto

public class ChildOtoto extends Parent {
    /** 年齢 */
    private int age;
    /** 名前 */
    private String name;

    public ChildOtoto() {
        name = "jiro";
        age = 10;
        favorit = "鼻を膨らます";
    }

    @Override
    public void say() {
        System.out.println(lastName + "といいます。");
        System.out.println("弟です。名前は" + name + "です。年齢は" + age + "です。");
        System.out.println("特技は、「" + favorit + "」です。");
    }

    @Override
    public void funny() {
        super.funny();
    }
}

MainFamilly

public class MainFamilly {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("**** Game Start ****");
        Parent parent = new Parent();
        ChildAni ani = new ChildAni();
        ChildOtoto ototo = new ChildOtoto();
        while (true) {
            String input = scan.nextLine();
            if ("parent".equals(input)) {
                parent.say();
            } else if ("ani".equals(input)) {
                ani.say();
            } else if ("ototo".equals(input)) {
                ototo.say();
            } else {
                System.out.println("処理を終了します。");
                break;
            }
        }
    }
}

ミッション1

まずは、MainFamillyクラスを起動してみましょう。
実行結果はどのようなものでしょうか?

ParentクラスChildAniクラスの違いは以下の部分です。

Parentのコンストラクタ

/** コンストラクタ */
public Parent() {
    lastName = "tanaka";
    name = "takao";
    age = 50;
    favorit = "ケツで箸を割る";
}

Parent#say()

/** 自己紹介 */
public void say() {
    System.out.println(lastName + "と申します。");
    System.out.println("親です。名前は" + name + "です。年齢は" + age + "です。");
    System.out.println("特技は、「" + favorit + "」です。");
}

ChildAniのコンストラクタ

public ChildOtoto() {
    name = "jiro";
    age = 10;
    favorit = "鼻を膨らます";
}

ChildAni#say()

@Override
public void say() {
    System.out.println(lastName + "です。");
    System.out.println("兄です。名前は" + name + "です。年齢は" + age + "です。");
    System.out.println("特技は、「" + favorit + "」です。");
}

ポイント

使用しているフィールド変数は全く同じものでしょうか?確認してみてください。アクセス修飾子をみて判断しましょう。
アクセス修飾子は下のような意味があります。

アクセス修飾子

public: どこからでも参照できる
protected: パッケージ内、クラスの継承関係がある場合に参照できる
private: クラスの中でのみ参照できる

まとめ1

アクセス修飾子によって、継承関係を作成したクラスの間で、参照する変数が変わります。
この特性を使用して、変数名が同じでも参照する変数が変えることができます。
「だからなに?」と聞かれそうですが、ここでの言いたいことはクラス固有の変数を持つことができるというところです。

ご先祖を作る

上記の記述では、Parent, ChildAni, ChildOtotoの3クラスを作成しました。ここまでで、クラスの継承に関しては理解できたと思います。注意点としてはアクセス修飾子です。この違いが理解できればオッケーです。

そして次の話に行きます。「ご先祖を作成する」というふうにきさいしましたが、抽象クラスを作成する方向で話を進めます。

抽象クラスとは

こちらの記事に記載されていたのは下のような文言です。

抽象クラスとは、抽象メソッドを 1 つ以上持つクラスです。そのクラスだけでは意味を持たず、サブクラスに継承されることで初めて機能します。

抽象メソッドとは、以下のように記述されるメソッドです。abstract の後に、戻り値の型、メソッド名、引数の型、引数の数のみを定義し、処理内容は実装されません。具体的な処理内容は、抽象クラスを継承するサブクラスで実装されます。

早い話が、下のように通常のクラスに「抽象メソッドを持っている」というところです。

アクセス修飾子 abtract 戻り値の型 メソッド名(引数); 

サンプルコード(ご先祖)

public abstract class Gosenzo {
    /** 秘伝 */
    public String hiden;

    public Gosenzo() {
        hiden = "秘伝のタレ";
    }

    /** 血継限界 */
    protected abstract String kekkeiGenkai();
}

このご先祖クラスを親クラスに継承します。
そうすると、ビルドエラーが出ます。

このビルドエラーは、「抽象メソッドのオーバーライド」を行ていないために出たエラーです。

つまり、抽象メソッドを実装することを強制できるというわけです。逆に言うとGosenzoクラスを継承したクラスは、必ずkekkeiGenkai()メソッドがあるということです。

当然、抽象クラスにはメソッドの中身(処理)を書いていないので、Gosenzoクラスを継承したクラスは、必ずkekkeiGenkai()を持っていることになります。

具体的に、ご先祖を継承したParentクラスの血継限界が「"Hello Japan!"」を標準出力へ出力するものでParent2クラスの血継限界が「"Good Night"」を標準出力へ出力するものだったとします。

そうしたときに、下のようなコードを使用するとGosenzoクラスの中小メソッドを使用することで、kekkeiGenkai()の処理内容を別のものにすることができます。

public static void main(String[] args) {
    Gosenzo g1 = new Parent();
    Gosenzo g2 = new Parent2();
    g1.kekkeiGenkai();
    g2.kekkeiGenkai();
}

これが、抽象クラスの扱い方の一つです。

しかし、、実際にはこのようなクラス関係を作成することはありません。わかりやすいように作っているだけです。具体的には「デザインパターン」を使用したり、自分で考えたクラス関係を特定の目的のために作ります。

次回予告

次回は、作成した上記のクラスを修正していろいろと動かしてみます。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ(Mac版)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

Java クラスの継承〜アクセス修飾子〜

クラスの継承

クラス継承の方法は、簡単です。下のようにコードを書けば良いです。

public class Child1 extends Parent { ... } 

継承関係を作ると、子クラスが親クラスの実装を受け継ぎ拡張することができます。

クラスの継承関係を作るときはクラス毎に役割を分担し、実装の依存関係を分断できるように作ります。詳細は下のリンクを参照下さい

Java クラスの扱い〜役割分担をする〜

クラスの書き方などは下のリンクを参照下さい。
Java Basic Class 〜クラスを作ってクラスを理解する〜
Java Basic Class 〜クラスを使うサンプル〜

ここからが本題です。

アクセス修飾子

継承関係を作成したあとには、フィールド変数や、メソッドを定義します。

これらは「メンバ」と呼ばれインスタンスを生成して使用する事が多いです。インスタンスを生成しない場合はstatic修飾子をつけて使用します。

前置きはさておきにして具体的な使い方などに触れていきます。

変数のスコープprivate

まずは、下の様にクラスの継承関係を作ります。

親クラス

public class Parent {
    protected static int kakei = 1000;
    protected Stirng lastName;
    private String name;
    private int age;
       /** コンストラクタ */
       public Parent() {
          lastName = "test";
          name = "taro";
          age = 50;
       }
    public void say() {
       System.out.println("My name is " + name + " " + lastName + " and " + age + "years old.");
    }
}

子クラス

public class Child1 {
    private String name;
    private String age;
    public Child1 {
       name = "polo";
       age = 15;
    }
    @Overeide
    public void say() {
      System.out.println("My name is " + name + " " + lastName + " and " + age + "years old.");
    }
}

メインメソッド

public static void main(String[] args) {
    Parent p = new Parent();
    p.say();
    Child1 c1 = new Child1();
    c1.say));
}

そして、上の様な処理を実装したら
どんな出力が得られるでしょうか?
注意点としては、アクセス修飾子により、アクセスするフィールド変数が異なるところです。実行して確かめてみてください。

変数のスコープprotected

次は、protectedについて見てみます。
ParentクラスのlastNameに着目してください。2つのクラスの出力は、「test」になったと思います。これは継承関係があるクラス間で共有出来る、スコープが継承関係のあるクラス間で使用出来る様になっているからです。※パッケージ内でも使える様です。
これらのクラスのコードをいじっていろんな出力を確認してみてください。
きっと、クラスの継承とはどんなものか?の答えが見えて来ると思います。
実際に作る時は、UML など使うとわかりやすいと思います。
例えば、RPGゲームを作ろうとした場合には、以下のような登場人物がいます。

  • ゲームプレイする「プレーヤー・キャラクター」
  • モンスターなどのコンピュータが操作する「ノンプレイヤー・キャラクター」

それぞれの登場人物を「クラス」で表現してみると継承関係を作るメリットがより明確に分かると思います。
Java テキストRPG(戦闘シーンのみ)を再作成する~LWJGLを参考に作り直す~
Java テキストRPG(戦闘シーンのみ)を再作成する2~設計からやり直す~

でわでわ。。。

関連ページ一覧

[Eclipse セットアップ](http://zenryokuservice.com/wp/2020/09/01/%e9%96%8b%e7%99%ba%e7%92%b0%e5%a2%83%e6%a7%8b%e7%af%89%ef%bd%9ewindows%e7%89%88eclipse%e3%81%ae%e8%a8%ad%e5%ae%9a%ef%bd%9e/)

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

Java Discord

  1. IntelliJ IDEA Discord Botを作る〜Gradle環境のセットアップ〜
  2. Java Discord セットアップ〜Hello Discord〜
  3. Java Discord ピンポン〜Discordプログラム〜
  4. Java Discord Listener実装〜コマンドを好きなだけ追加しよう〜