Java ポリモーフィズム(多様性) 〜実装をしてみる〜

イントロダクション

前回の学習を踏まえて、抽象クラス、インターフェースを使用して簡単なプログラムを作成してみようと考えています。

頭で理解しても、実践が伴わなければものになりません

前回の学習ポイント

継承関係を作り、下の図のようなクラス関係を作成しました。

そして、RpgCharacterクラスは、抽象クラスです。

以下のような手順で作成しました。

!. 通常のlクラスを作成する(RpgCharacter)

  1. 必要なプロパティ(属性を追加する)
  2. それぞれのGeter&Setterを作成
  3. このクラスを継承するすべてのクラスに共通するであろうメソッドを作成
    /** にげる */
    public void escape() {
    System.out.println(this.name + "は逃げ出した");
    }
  4. 実装する各クラスによって別の処理をさせたいので、抽象メソッドを作成
    /** 抽象メソッド「たたかう」 */
    public void attack(Monster monst);
  5. RpgCharacterクラスを継承して、Wizardクラスなどのクラスを作成する

このように、クラス関係を作ると、RpgCharacterクラスを継承したクラスを全てRpgCharacterクラス型の変数で各クラスオブジェクトを使用することができる。
<RpgCharacterクラス型の変数でいろんなクラスを使う>

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() + "さん");
        }
    }

ポリモーフィズムの実装

前回学習したものを実装して、「動くもの」を作成しようというところです。

実装する手順を考える

実装するのには、それなりの時間がかかりますが、やってみることの意義はすごく大きいです。

今回は、キャラクターを作成する処理から実装していこうと考えています。

<作成するものVersion1>

  1. 勇者とモンスターが1回ずつ攻撃する
  2. それぞれの攻撃に対して、各キャラクターのHPを減算する

メインメソッドの実装

ここでは、プログラムをイチから作成する方向で、話を進めていきます。

作成したプログラムは下のような実行結果が得られるように、実装します。

まずは、プログラムを実行するためのクラス(BattleSample.java)を作成します。エラーが出る部分はコメントアウトします。

つまるところは、どのような処理を呼び出したいかを実装します。ちなみにコメントのみでも良いです。

バージョン1の実装

<BattleSample.java-バージョン1

public static void main(String[] args) {
    //勇者クラスの生成 
    RpgHero hero = new RpgHero("勇者");
    // モンスタークラスの生成
    Slime slime = new Slime("スライムA");

    // 勇者の攻撃
    hero.attack(slime);
    // モンスターの攻撃
    slime.attack(hero);
}

このように、勇者クラスと、スライムクラスをインスタンス化してそれぞれのメソッド(コマンド)を呼び出してやるだけのシンプルな実装です。

これで、勇者とスライムの攻防がコンソール(標準出力)へ出力されます。

問題は、RpgHeroクラスとSlimeクラスができていないという部分です(前回の実装を行った人は作成済みです)

RpgHeroクラスを作る

このクラスは、下の図にあるように、RpgCharacterクラスを継承した形で、実装します。

なので、先にRpgCharacterクラスを作成する必要がありますので、こちらのクラスの製造に着手します。

RpgCharacterクラスの製造

このクラスは、湯者だけではなく魔法使いや、盗賊、戦士などのプレーヤー側のキャラクターに共通する部分を実装する
想定で、設計しました。

なので、下のように共通するもの(属性・振る舞い)を実装します。

つまり、現状では、通常のクラスになります。

public class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;
   ・
   ・
   ・
}

上の状態では、属性(フィールド変数)を定義した状態です。

このほかに追加したいものもあるので、一度内容を確認します。

<RpgCharacterクラスの設計1>

  1. 属性、名前を持っている
  2. 属性、MPを持っている
  3. 属性、年齢を持っている
  4. 属性、性別を持っている
  5. 属性、誕生日を持っている

個の状態では、共通するプロパティ(属性)のみが定義されています。ほかにも考えれば出てきそうですが。。。

そして、共通するコマンド(メソッド)を定義します。

<RpgCharacterクラスの設計2>

  1. 属性、名前を持っている
  2. 属性、MPを持っている
  3. 属性、年齢を持っている
  4. 属性、性別を持っている
  5. 属性、誕生日を持っている
  6. 振る舞い、にげるを持っている

作成した設計を元に、そのクラスを実装します。

<RpgCharacter>バージョン1

public class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;

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

フィールド変数には、本来「private」修飾氏をつけるのですが、今回は、子クラスを作成するための親クラスとして作成しているので、「protected」をつけて、継承関係のあるクラスであれば参照可能な形にしています。

そして、Getter, Setterは省略していますので、それぞれのメソッドが実装されているていで話を進めます。

この状態では、「たたかう」メソッドを強制的に継承する子クラスに実装させることができないので、下のような、抽象メソッドを追加します。

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

このようにすると、「抽象メソッドは抽象クラス、インターフェースでないともていない」ので、RpgCharacterクラスを通常のクラスから「抽象クラス」に変更します。

public abstract class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 攻撃力 */
    protected int attackPower;
    /** 防御力 */
    protected int diffencePower;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;

    /** 使用できるコマンドのメニューを開く */
    public abstract void showMenu();
    /** たたかう */
    public abstract void attack(RpgCharacter character);
    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }

そして、このクラスをインスタンス化するとき(newするとき)にデフォルトの値を設定するための

コンストラクタを定義(実装)します。引数に名前だけを与えた場合「コンストラクタ1」と、引数にすべてのプロパティ(属性)を与えた場合「コンストラクタ2」を定義しました。

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

    /** コンストラクタ2 */
    public RpgCharacter(String name, int sex, int age, String birthDay) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthDay = birthDay;
    }

RpgHeroを作る

ここでRpgHeroクラスの製造に戻ります。

ここまできたら、シンプルにRpgCharacterクラスを継承したRpgHeroクラスを作成するだけです。抽象メソッドのオーバーライドがありますが。。。

public class RpgHero extends RpgCharacter {
    /** コンストラクタ */
    public RpgHero(String name) {
        super(name);
    }
    /** コンストラクタ */
    public RpgHero(String name, int sex, int age, String birthDay) {
        super(name, sex, age, birthDay);
    }

    @Override
    public void attack(RpgCharacter monst) {
        System.out.println(this.name + "の攻撃");
        int damage = getAttackPower() - monst.getDiffencePower();
        System.out.println(monst.getName() + "へ" + damage + "のダメージ");
        monst.setHp(monst.getHp() - damage);
    }
}

継承したクラス(子クラス)から親クラスのコンストラクタ、メソッド、フィールドを呼び出したい場合は「super」をつけてやります。

同様に自分のクラス(子クラス)で保持するもの(コンストラクタ、メソッド、フィールド)を呼び出したい場合は「this」をつけてやります。

そして「@Override」というアノテーションがついているメソッドは、抽象メソッドを「オーバーライド」していることを明示的に示しています。

これと同様に、MonsterクラスとSlimeクラスも作成してしまいましょう。

Monsterクラスの製造

public class Monster extends RpgCharacter {

    /**
     * @param name
     */
    public Monster(String name) {
        super(name);
    }

    @Override
    public void attack(RpgCharacter player) {
        System.out.println(this.name + "の攻撃");
        int damage = getAttackPower() - player.getDiffencePower();
        System.out.println(player.getName() + "へ" + damage + "のダメージ");
        player.setHp(player.getHp() - damage);
    }
}

必要な属性(フィールド)はRpgCharacterクラスで持っているので、このクラスに特有な部分のみを実装します。

そして、コンストラクタに関しては、細かい情報(性別、年齢など)は必要な時にsetAge()などのメソッドで設定すれば良いので明示できに書きません。

その代わり、RpgCharacterクラスの、初期値としては「不明」「-1」などの値を設定するようにします。

int型は、参照型のようなnullという値が使えないので、-1で代用します。

つまりは、「年齢が『-1』だった場合『年齢不詳』という意味にする」というルールを追加してやれば、この問題は解決出るのでそのようにしたというところです。

スライムクラス

public class Slime extends Monster {

    /**
     * @param name
     */
    public Slime(String name) {
        super(name);
    }
}

このクラスに至っては、コンストラクタのみの実装で事足ります。

<<<前回

継承に関するページ一覧

  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プログラミング〜

android Tesseract エラー: シンボルを見つけられません

エラー: シンボルを見つけられません

シンボル: クラス Size
場所: クラス com.googlecode.leptonica.android.Pix

エラー: パッケージandroid.support.annotationは存在しません

上のようなエラーが出ました。同様に下のようなエラーも出ました。

同様に、以下のクラスも見つかりませんでした。

  • com.googlecode.leptonica.android.Binarize
  • com.googlecode.leptonica.android.Box
  • com.googlecode.leptonica.android.Boxa
  • com.googlecode.leptonica.android.Constants
  • com.googlecode.leptonica.android.Edge
  • com.googlecode.leptonica.android.JpegIO
  • com.googlecode.leptonica.android.MorphApp
  • com.googlecode.leptonica.android.Rotate
  • com.googlecode.tesseract.android.TessBaseAPI.PageSegMode

解決策

プロジェクトのbuild.gradleに以下のコードを追加する

implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

そして、足りないパッケージの追加は下の画面よりできるはず。。。

Java じゃんけんゲームを作ってみる ~基本文法をマスターする~

じゃんけんゲーム

Javaプログラミングの基本文法を理解するためにも、実際にアプリケーションを作ってみるのが手っ取り早い方法です。

教科書などを眺めても、実践に勝る学習はありません。つまりは。。。

経験に勝る知識なし!

ということです。ちなみに、

サイトマップはこちらです。

Javaプログラム作成時の(推奨する)ルール

  1. クラスの名前ははじめを大文字にして、アルファベットを使用しましょう。キャメルケースといいます

    Ex: SampleClass, FirstClass, MySample ...

  2. メソッドの名前ははじめを小文字にして、アルファベットを使用しましょう。メソッドの場合は、うしろに「()」を付けます。

    Ex: myMethod(), firstMethod(), firstTest(), handleRequest() ...
    クラス名+メソッド名で書きたい場合は「クラス名#メソッド名()」のように書きます。
    例:MyClass#myMethod()

  3. 「{」を一つ入力したら、TAB(インデント)をいれましょう。「}」を入力したらTAB(インデント)を減らしましょう。

    public class FirstTest {
    public static void main(String[] args) {
        // 「{」が2個あるのでTABが2つあります。
        System.out.println("Hello World");
    }
    // 「}」が一つ出てきたので、TABが1つあります。
    }

    これらのルールは、Javaプログラミングの基本的なコーディングルールになるので、ほとんどの会社さんでも使用しているルールです。
    早い話が、現場でも使えるということです。※現場により使えないこともあります。

じゃんけんゲームの処理内容

本来は、自分で考えて仕様を考えるべきですが、初めの一歩ということで仕様を提示します。

仕様とは、「〇〇はXXのように動く」ということを一つ一つ決めて(定義して)、どう動くのか決めることです。

作成するクラスは「JankenPon」クラスにします。

じゃんけんゲームの仕様

  1. 標準入力での、0:グー、1:チョキ、2:パーと定義する。つまり、0が入力されたら「グー」1ならば「チョキ」、2ならば「パー」と判断する
  2. ユーザーの手(入力)とCPUの手(乱数)を表示する
  3. 勝敗判定を行い、「YOU_WIN」、「YOU_LOSE」「DRAW」を表示する

この様に使用を決めたらそれぞれの項目(要件)を満たすようにプログラムを作ります。

0:グー、1:チョキ、2:パーと定義する

標準入力を受け取り、グー~パーを判定します。
どのように実装したらよいでしょうか?

今回は、試しに以下のような実装をしてみましょう。

if文をマスターする

マスターするといっても、普通に使うだけですが。。。
ポイント
次のプログラムは、標準入力を受け付けて、1行分のデータ(入力)を取得します。

Scanner scan = new Scanner(System.in);
scan.nextLine();

ちなみに、下のコードでは、数字以外の入力を行うとエラーが出ます。

public class FirstSample {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int input = Integer.parseInt(scan.nextLine());
        String jankenTe = null;
        if (input == 0) {
            jankenTe = "グー";
        } else if (input == 1) {
            jankenTe = "チョキ";
        } else if (input == 2) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
    }
}

じゃんけんの手をString型の変数JankenTeに設定(代入)します。
0, 1, 2以外の値が入力されたときは想定外の値です。

IF文を使用したときの動画

ユーザーの手(入力)とCPUの手(乱数)を表示する

CPUの手を乱数を使って表示します。これはもう説明する必要はないと思います。

public class SecondSample {
    public static void main(String[] args) {
        // 0-2の乱数を生成する
        cpuTe = String.valueOf(new Random().nextInt(2));

        if (cpuTe.equals("0")) {
            jankenCpu = "グー";
        } else if (cpuTe.equals("1")) {
            jankenCpu = "チョキ";
        } else if (cpuTe.equals("2")) {
            jankenCpu = "パー";
        }
    }
}

これも、if文で実装することができます。もちろん、より良いアイディアがあればそちらの実装を行うのが正しい判断です。検証するのを忘れないようにしてください。

勝敗判定を行い、「YOU_WIN」か「YOU_LOSE」「DRAW」を表示する

勝敗判定は、ちょっと良いアイディアが出ず。。。そのまま判定処理を作りました。
はっきり言ってダメダメなコードです。

public class ThirdSample {
    public static void main(String[] args) {
        String hantei = input + cpuTe;
        String result = null;
        if (hantei.equals("01")) {
            result = "YOU_WIN";
        } else if (hantei.equals("12")) {
            result = "YOU_WIN";
        } else if (hantei.equals("20")) {
            result = "YOU_WIN";
        } else if (hantei.equals("00")) {
            result = "DRAW";
        } else if (hantei.equals("11")) {
            result = "DRAW";
        } else if (hantei.equals("22")) {
            result = "DRAW";
        } else if (hantei.equals("02")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("10")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("21")) {
            result = "YOU_LOSE";
        } else {
            System.out.println("想定外の値です。" + hantei);
        }
    }
}

何とかじゃんけんゲームができました。しかし、このようなプログラムは美しくありません。

別に「アーティスチックでないからダメ」といっているわけではなく、見ずらいし、わかりずらい。

人にやさしくないプログラムが「ダメダメ」だということです。

人にやさしく

ズバリ、読みやすく、理解しやすいコードが良いブログラムです。もちろん、余計なメモリを使わないとかいろいろありますが。。。

ちなみに、上のようなコードで作成したじゃんけんゲームは以下のようなコードになりました。

public static void main(String[] args) {
    // 1.初めの処理
    System.out.println("じゃんけん ...");
    Scanner scan = new Scanner(System.in);

    // 2.標準入力受付
    String input = scan.nextLine();
    String jankenTe = null;

    // 3.ユーザーの手を判定する
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }

    String cpuTe = null;
    String jankenCpu = null;
    if (jankenTe == null) {
        // 強制終了
        System.exit(-1);
    } else {
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));

        if (cpuTe.equals("0")) {
            jankenCpu = "グー";
        } else if (cpuTe.equals("1")) {
            jankenCpu = "チョキ";
        } else if (cpuTe.equals("2")) {
            jankenCpu = "パー";
        }
    }
    System.out.println("ポン");
    System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

    // 5.勝敗判定を行う
    String hantei = input + cpuTe;
    String result = null;
    if (hantei.equals("01")) {
        result = "YOU_WIN";
    } else if (hantei.equals("12")) {
        result = "YOU_WIN";
    } else if (hantei.equals("20")) {
        result = "YOU_WIN";
    } else if (hantei.equals("00")) {
        result = "DRAW";
    } else if (hantei.equals("11")) {
        result = "DRAW";
    } else if (hantei.equals("22")) {
        result = "DRAW";
    } else if (hantei.equals("02")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("10")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("21")) {
        result = "YOU_LOSE";
    } else {
        System.out.println("想定外の値です。" + hantei);
    }
    // 6. 結果の表示
    System.out.println(hantei);
    System.out.println(result);
}

別の記事になりますが、「じゃんけんゲーム in コンソール」の記事になります。

しかし、やはりだめなコード(個人的にそのように思う)なので、これをまともなコードに直します。

この様な行為のことをリファクタリングといいます。

リファクタリング

まずは、読みずらいところを洗い出します。
以下の部分が読みずらいと思いました。

if文のネスト(階層化しているところ)が無駄なネストになっている

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
} else {
    // 4.CPUの手を判定する
    cpuTe = String.valueOf(new Random().nextInt(2));

    if (cpuTe.equals("0")) {
        jankenCpu = "グー";
    } else if (cpuTe.equals("1")) {
        jankenCpu = "チョキ";
    } else if (cpuTe.equals("2")) {
        jankenCpu = "パー";
    }
}

どこが、無駄かというとelse~の部分です。もしjannkenTeがnullになっているのならば、プログラムを強制終了するので、if-elseでわける意味がありません。無駄なコードになっているのです。

ではどのようになおすか?ズバリ下のように直します。elseが丸まる必要ないのです。

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
}

// 4.CPUの手を判定する
cpuTe = String.valueOf(new Random().nextInt(2));

if (cpuTe.equals("0")) {
    jankenCpu = "グー";
} else if (cpuTe.equals("1")) {
    jankenCpu = "チョキ";
} else if (cpuTe.equals("2")) {
    jankenCpu = "パー";
}

処理の内容を考えてみれば、不要ですよね?なぜなら

System.exit(-1);

でプログラムは強制終了(引数に「-1」を与えているので『異常終了』)するからです。

つまり、if文の中の処理を行うとき=jankenTeがnullの時は「CPUの手を判定」より後の処理が行われません。

メソッドを利用する

その他にも、イケていないところがあります。それは、似たような処理がたくさんあるというところです。
特にif文の処理が同じような処理を行っています。

なので、メソッドを作成して同じコードを書かないようにします。

今までに、ちょこっと書いたりしました。改めて理解しましょう。

具体的には、以下のif文です。2か所、同じコードがあります。

String jankenTe = null;
if (input.equals("0")) {
    jankenTe = "グー";
} else if (input.equals("1")) {
    jankenTe = "チョキ";
} else if (input.equals("2")) {
    jankenTe = "パー";
} else {
    System.out.println("想定外の値です。" + input);
}

これをメソッドとして切り出します。具体的には下のように切り出します。staticがついているのはメインメソッドから呼び出すためです。

public static String jankenHantei(String input) {
    String jankenTe = null;
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }
    return jankenTe;
}

そして、このコードと同じ部分を書き換えます。下のようなコードになりました。

public class JankenPon {
    public static void main(String[] args) {
        // 1.初めの処理
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = null;
        if (hantei.equals("01")) {
            result = "YOU_WIN";
        } else if (hantei.equals("12")) {
            result = "YOU_WIN";
        } else if (hantei.equals("20")) {
            result = "YOU_WIN";
        } else if (hantei.equals("00")) {
            result = "DRAW";
        } else if (hantei.equals("11")) {
            result = "DRAW";
        } else if (hantei.equals("22")) {
            result = "DRAW";
        } else if (hantei.equals("02")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("10")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("21")) {
            result = "YOU_LOSE";
        } else {
            System.out.println("想定外の値です。" + hantei);
        }
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }
}

これで、多少はよくなりました。しかし、まだです。

Mapインターフェースを使用する

じゃんけんの判定方法ですが、すごく頭の悪いやり方です。しかし、これから見せるコードも頭の良いやり方ではありません。なぜかというと、何の工夫もないからです。

頭の良いやり方ではない方法
まずは下のような、フィールド変数(定数)とメソッドを作成します。

<フィールド変数とは>
Javaクラスの持つことのできる要素の一つです。
具体的には、フィールド変数は、変数であり、クラスの中であればどこでも使用することができます。
UMLという表現方法では、下のように書きます。
Diagram1

同時に、クラスが持つ要素を表します。「属性」はフィールド変数、「操作」はメソッドを示します。
例えば、ボタンを押したらカウンターをカウントアップするアプリケーションを考えてみましょう。
「ボタンを押す」という操作(メソッド)を使用すると、「カウンター」という属性を1カウントアップ(プラス1)するという具合に、各要素を使用します。

つまりは、以下のような特徴があります。

  • フィールド変数はデータの管理、他のクラスを保持する変数。
  • メソッドは何かしらの処理(操作)を行うためのプログラムをまとめたもの。

<実際のコード>

public class SampleClass {
    /** フィールド変数(定数) */
    public static final String YOU_WIN = "YOU_WIN";
    public static final String YOU_LOSE = "YOU_LOSE";
    public static final String DRAW = "DRAW";</pre>

    // そして、次のメソッドを追加します。
    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }
}

作成したメソッドを使用して、メインメソッドを書き直します。

そうすると下のようになりました。

public class Chap0 {
    public static final String YOU_WIN = "YOU_WIN";
    public static final String YOU_LOSE = "YOU_LOSE";
    public static final String DRAW = "DRAW";

    public static void main(String[] args) {
        // 1.初めの処理
        Map<String, String> hanteiMap = createMap();
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU: " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = hanteiMap.get(hantei);
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }

    public static Map<String, String> createMap() {
        Map<String, String> hanteiMap = new HashMap<String, String>();
        hanteiMap.put("01", YOU_WIN);
        hanteiMap.put("12", YOU_WIN);
        hanteiMap.put("20", YOU_WIN);
        hanteiMap.put("00", DRAW);
        hanteiMap.put("11", DRAW);
        hanteiMap.put("22", DRAW);
        hanteiMap.put("02", YOU_LOSE);
        hanteiMap.put("10", YOU_LOSE);
        hanteiMap.put("21", YOU_LOSE);

        return hanteiMap;
    }
}

実行結果は下のような形で出力されました。

じゃんけん ...
1
ポン
あなた: チョキ CPU; チョキ
11
DRAW

如何でしょうか?だいぶすっきりしたように見えると思います。プログラムの処理内容が理解できていれば、無図解ことではありません。「このような方法もある」というのを知っておくと後々にカッコよいコードが書けるようになります。※自分も努力いたします。。。

<スマホアプリ版じゃんけんゲーム>※GLUONを使用

でわでわ。。。

Windows Cドライブの節約をする ~シンボリックを貼る~

Cドライブの節約をする

WindowsのCドライブがいっぱいになりそうなときシンボリックリンクを使用して問題の容量不足を解消できます。

正確には、ドライブの仕様先を別なドライブに変更するということです。

シンボリックリンク

例えば、Google Chormeなどのアプリケーションを使用すると「 」にデータが蓄積されていきます。

これでは、少ないCドライブにデータを保存せずに、指定した場所に保存する事ができるようになります。

前提条件

  1. シンボリックリンクを作成するフォルダが存在しないこと
  2. 変更先(新しく保存する場所)にファイルが存在していること
  3. 管理sが権限でコマンドプロンプトを起動する

上記の注意点を踏まえて、下のコマンドを実行します。

mklink /d "シンボリックリンクを作成するフォルダのパス" "新しく保存する場所"

具体的には下のようなコマンドを実行しました。

mklink /d "C:\Users\Takunoji\AppData\Local\Android" "D:\AndroidStudio4.1"

以上です。

Andoroid アプリを作る ~Javaでスマホアプリ環境構築~

Andoroid アプリを作る

そろそろと、Androidアプリの作成に入りたいと思います。
今までに考えてきた、目標達成アプリの作成に着手します。細かい部分に関しては、Gitに記載しています。

Android Studioのインストール

Androidのダウンロードページからインストーラーをダウンロードします。

ダウンロードしたインストーラーを起動します。

インストールディレクトリの指定以外はそのまま次へを選択しました。

AndroidStudio

起動すると下のような画面が開きます。

セットアップウィザードで、SDKのインストール先を聞かれますが、これもそのままの値を使用します。

つまりは、ディレクトリの設定以外はいじらないということです。

そして、インストール完了、結構時間がかかりました。

まずは。。。

Androidのアプリ作成について理解します。ここでのポイントは、大まかに理解するところです。

アプリ作成の土台になるAndoroidのアプリはフレームワークになっていて、多少の仕組みがあります。その一つにライフサイクルというものがあり、画面がどのように開いて、閉じて、アプリを終了するか?
その処理の流れを決定しているものが「フレームワーク」です。

  1. Androidのアーキテクチャ
  2. Java Android 〜スマホアプリではじめるJava〜
  3. Java Android 2〜Androidアプリの見た目を作る〜
  4. Java Android 3〜Androidアプリの見た目を変更する〜
  5. Java Android 6〜計算アプリのJavaコードを書く〜

プロジェクトの作成

「Create New Project」を選択します。

そして、Empty Activityを選択します。

さらに、プロジェクト名を入力します。日本語にすると文字化けしたときに面倒なので英語にしました。
あと、使用するプログラミング言語を選択できるのでJavaを選択しました。
Javaだと、OpenCVや、OpenGLなどと連携。。。Kotlinでもできるのかもしれませんが、大体のことは大体できるようになるので、こちらの言語を選択しました。

Androidの最低バージョンの選択として、6(Nougat)約5年前(2020-02-12現在)を選択しました。

一番下のチェックボックスはチェックを入れませんでした。有料ライブラリもあるようなので、後程追加する方向で作業を行おうと考えました。

ワークスペース(開発の画面)が開いたら、しばらくGradleのインストールなどの処理が走りますのでちょっと待機します。
下の方に、次のようなプログレスバーが見れます。

とりあえずは動かしてみました。次は環境設定を行います。

よくよく見るとビルドエラーが出ています。

Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:

これに関しては、こちらのページに詳細があるようです。

そして、EventLogの部分に詳細なエラーないようがあるのでそれを見て解決します。

1 error
Consult IDE log for more details (Help | Show Log) (2 m 45 s 517 ms)
8:51 Android Studio is using the following JDK location when running Gradle:
D:\Apps\AndroidStudio\jre
Using different JDK locations on different processes might cause Gradle to

そして、調べてみるとおかしなところに気が付きました。

参考サイト

おかしなところ

  1. AndroidStudioのインストール先をDドライブにした。
  2. JDKの設定をしていない

上のものは、自分の予想でしたが、JDKの設定以外は関係ありませんでした。

JDKの設定

File -> Project Structure

実際に使用しているJDKとインストールしたSDKを指定します。

しかしまだエラーが出ます。

import org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder

そして

unable to resolve class org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder

つまるところは、このクラスのインポートが必要ということで。。。

Gradleで依存関係の追加を行う必要があるということでした。Gradleの使い方はこちらのサイトを参考にしました。

下のコードをbuild.gradleに追加します。コピー元はこちらです。

/**
 * The buildscript block is where you configure the repositories and
 * dependencies for Gradle itself—meaning, you should not include dependencies
 * for your modules here. For example, this block includes the Android plugin for
 * Gradle as a dependency because it provides the additional instructions Gradle
 * needs to build Android app modules.
 */

buildscript {

    /**
     * The repositories block configures the repositories Gradle uses to
     * search or download the dependencies. Gradle pre-configures support for remote
     * repositories such as JCenter, Maven Central, and Ivy. You can also use local
     * repositories or define your own remote repositories. The code below defines
     * JCenter as the repository Gradle should use to look for its dependencies.
     *
     * New projects created using Android Studio 3.0 and higher also include
     * Google's Maven repository.
     */

    repositories {
        google()
        jcenter()
    }

    /**
     * The dependencies block configures the dependencies Gradle needs to use
     * to build your project. The following line adds Android plugin for Gradle
     * version 4.0.0 as a classpath dependency.
     */

    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.0'
    }
}

/**
 * The allprojects block is where you configure the repositories and
 * dependencies used by all modules in your project, such as third-party plugins
 * or libraries. However, you should configure module-specific dependencies in
 * each module-level build.gradle file. For new projects, Android Studio
 * includes JCenter and Google's Maven repository by default, but it does not
 * configure any dependencies (unless you select a template that requires some).
 */

allprojects {
   repositories {
       google()
       jcenter()
   }
}

そうするとAndroidStudioに下のようなダイアログ?が出ます。

クリックしますとビルドが始まりマス。

これでもダメでした。。。

調べてみると、Android4.1での問題があるようです。こちらの記事に詳細がありますのでそれを参照すると、AndroidStudioをアップグレードしてください、ということだったのでそのようにしようとしました。

ディスクの空き容量の問題で断念しました。仕方ないので別のPCで実装することにします。。。
別のPCはMACです。

でわでわ。。。

関連記事一覧