イントロダクション
前回は、継承に関して学習しました。
継承関係を作る目的は大まかに下の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ゲームを作成しようとしているので下のようなクラスを作成したいと思っている。
- 勇者・クラス
- 魔法使い・クラス
3.盗賊・クラス - 超勇者クラス
- マタンゴ・クラス
- ポインズンマタンゴ・クラス
- ゴブリン・クラス
- ウェアウルフ・クラス
- デスバット・クラス
これらのクラスを作るのに、全てのクラスに「HP」「名前」が必要になりますが、毎回同じコードを書くのは
スマートではありませんので「継承(extends)」を行うことでこれらを解消することができました。
※↑前回の学習内容
そして、インターフェースも同様に「同じカテゴリ」のクラスを作ることができます。
具体的には、自分が作成した「CommandIF」のことです。
今回の学習ポイント
- 今回は、通常のクラスにある、メソッドとは別の「抽象メソッド」に関して学びます。
- 通常のクラス + 「抽象メソッド」= 【抽象クラス】
- 「抽象メソッド」と定数のみ = 【インターフェース】
未来に備えるための継承
プログラミングを行う目的には、大まかに2種類あります。
- 目的のものを実装する(今作ろうとしているものを作る)
- 今後あると便利なものを前もって、実装しやすいように作っておく
※普段から今後使えるものを作ろうとするのは基本ですが。。。
先ほどの具体例Aを例にすると、下のようなクラス群を作成するのに共通するプロパティ(属性)を持ったクラスを継承して作成するのが、良さそうです。※この時点では、アイディアのレベルで良いのです。
- 勇者・クラス
- 魔法使い・クラス
3.盗賊・クラス - 超勇者クラス
- マタンゴ・クラス
- ポインズンマタンゴ・クラス
- ゴブリン・クラス
- ウェアウルフ・クラス
- デスバット・クラス
なので、これらのクラスに共通するプロパティ(属性)を考えます。
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ポイントのダメージをあたえた!");
}
継承に関するページ一覧
- Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
- UMLの書き方(読み方)〜概要とクラス図〜
- Java クラスの継承を理解する
- クラスの継承〜アクセス修飾子〜
- クラスの継承関係を作る1
- クラスの継承関係を作る2
環境構築関連ページ一覧
設計関連ページ一覧
PHP関連ページ
- WordPress プラグイン作成〜DBを使用する〜
- PHP PDO 〜MySQLにアクセスする〜
- PHP Ajax 〜DBに登録したデータを受信する〜
- Google Maps API PHP連携 〜マップ情報をDBに登録する〜
- PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
- AngularJS Routing 〜PHPをWeb APIにする〜
- WordPress PHPカスタム〜根本的に見た目を変える〜
- WordPress PHPカスタム〜根本的に見た目を変える2〜
- Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
- WordPress テスト実装 〜heade-test.phpを表示〜
- AngularJS + PHP 〜WordPressと連携する〜
- AngularJS + PHP 〜AngularJSの実装〜
- AngularJS + PHP 〜AngularJSの実装2〜
- WordPress 処理解析 ~index.phpを眺める~
- WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
- WordPress 処理解析 ~ログイン処理を調べる~
- WordPressカスタム〜アンケートボタンを追加する(設計)〜
- WordPressカスタム〜プラグインの作成〜
- WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
- WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
- WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
- WordPress プラグイン作成〜アンケート作成プラグインを作る〜
JS関連ページ
- JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
- 吹き出しにYoubetubeを埋め込む
- Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
- JS XmlHttpRequest 〜JSでの同期/非同期通信〜
- JS Google Maps API 〜GeoLocation 現在位置の取得〜
- AngularJS + PHP 〜AngularJSの実装〜
- AngularJS + PHP 〜AngularJSの実装2〜
- WordPress プラグイン作成 〜$wpdbでのSELECT〜
- WordPressプラグイン作成 〜HTML挿入まで完了〜
- WordPress プラグイン作成 〜アンケート挿入〜
- MAMP 起動設定 〜WordPressのテスト環境を作る〜
- MAMP WordPress 〜インポート時のエラー対処〜
- WordPress PHPカスタム〜根本的に見た目を変える2〜