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プログラムは「型」を重視するので、データ型は厳密です。これがはっきりしていないと、デバックするときに変数の中身のデータ型を確認するためにコードを追いかけなくてはいけません。。。

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

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

<抽象クラスを使った例>
Calendarクラスはインスタンスを取得するときに「カレンダ・フィールドは現在の日付と時間に初期化」されるため。
「getInstance()」でインスタンスを取得する。

<インタフェースを使用したポリモーフィズムの例>

例外処理

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

==エラーの種類==

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

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

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

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

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

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

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

File入出力

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

<ラップしない場合>

/** ラップされるメソッド */
public void rap(String fileName) throws Exception {
    File file = new File(fileName);
    BufferedWriter writer = new BufferedWriter(new FileWriter(file));
    writer.write("aaa");
    writer.close();
}
/** 実行するメソッド */
public void execute() {
    try {
        rap("./test.txt");
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(-1);
    }
}

<ラップする場合:rapped()がrap()をラップしている>

/** ラップされるメソッド */
public void rap(String fileName) throws Exception {
    File file = new File(fileName);
    BufferedWriter writer = new BufferedWriter(new FileWriter(file));
    writer.write("aaa");
    writer.close();
}
/** ラップするメソッド */
public void rapped(String fileName) {
    try {
        rap(fileName);
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(-1);
    }
}

/** 実行するメソッド */
public void execute() {
    rapped();
}

rap()メソッドをラップすることで「try~catch」で囲まなくてもよくなりました。欠点としてはエラーハンドリングができなくなったことです。

File入出力(File IO)

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

File出力(Writerで書き込み)

File入力(Readerで読込)

プロパティファイルを読む

<プロパティファイルを読み込む(全部)>※File入力の処理です。

ここの処理では、Readerを使用せず、Propertiesクラスの「load()」メソッドでファイルを読み込んでくれるのでこれを使用します。

プロパティファイルの読み込み

コード

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を作る」ということをテーマ(話題)として話を進めていきます。

テキストRPG(戦闘シーンのみ)1

テキストRPG(戦闘シーンのみ)2

前回までに作成したもの

まとめると、キャラクターを表現するクラスを作成しました。RpgCharacterクラスを
継承して、必要なパラメータ、HPなどを定義しておき、勇者などのクラスは固有のパラメータのみを保持する形で実装しました。

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

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

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

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

ポリモーフィズムの設計

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

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

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

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)>

Socket通信を使用した、実装のサンプルです。これは画面(サーバー)C++で作られています。しかし、クライアント側、つまりアクセスする方はJavaでさくせいしました。実行はEclipseを使用しています。

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

  • 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の基本文法の理解をしている

今回は、クラスの継承関係を作成しその関係について記載しました。ここでいう基本文法というのは「条件分岐」と「ループ」「extendsの書き方」「implementsの書き方」です。

継承関係を作って実行してみました。 => 親、兄、弟のクラスを作成して実行した動画です。

クラスの継承

クラスの継承を行う目的は大まかに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();
    }
}

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

クラスを継承することでできること

この記事では次のことを記載しています。

  1. クラスをグループ化する
  2. クラスを拡張する

クラスをグループ化する

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

なので、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();
    }
}

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

そして、このグループ化に関してはインタフェースでも行うことができます。下の図はクラス図で表現した場合のものです。
Parentというグループに兄と弟が存在している形になります。
そして、弟は「Interface1」というグループにも所属している形になります。

クラスを拡張する

そして、「クラスを継承する目的」その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.カプセル化:処理などをまとめること、外部から隠蔽する事

2.抽象化:対象から小さな特徴を除いて、本質的な特徴だけを集めた状態にする

3.ポリモーフィズム:同じインターフェースでありながらデータ型に合わせて異なる動作をする機能

4.継承:フィールド、メソッドを受け継ぐことができる機能。

絵にすると下のようなイメージです。

そして、JButtonクラスを継承して、オリジナルのボタンクラスを作成したときの動画です。

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

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

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

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

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

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

実現するべきこと

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

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

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

2. オブジェクトとは

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

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

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


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

役割分担を行い、それぞれの役割に状態管理をするためのフィールド(情報)を持たせます。プログラムコードにすると下のような形です。

public class DengenKakari {
   /** 電源 **/
   private boolean switchON;

   /** 電源をON,OFFにする */
   public void switch(boolean switch) {
        this.switchOn = switch;
   }
}
係(役目) 情報(状態) 機能
電源係 電源 電源を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

抽象クラスは「new」できないから「getInstance()」メソッドを使用してインスタンスを取得する

クラスと継承

クラス

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

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

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

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

図C

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

インスタンス化

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

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

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

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

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

継承

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

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

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

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

JavaSwingの例

SampleClassクラスにJFrameクラスを継承しました。その時の実装動画です。

下の図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〜クラスとは〜