前回、FF5のジョブをクラスとしてインスタンス化することを書きましたが、
今回はその続きです。
おさらいがてらジョブをクラスとして宣言していきます。
public class ナイト {
public void たたかう() {
// 装備している武器で攻撃する
}
public void ぼうぎょ() {
// 身を守り、ダメージを軽減する
}
public void 両手持ち() {
// 武器を両手で持ち、攻撃力を倍増させる
}
・
・
・
}
public class シーフ {
public void たたかう() {
// 装備している武器で攻撃する
}
public void ぼうぎょ() {
// 身を守り、ダメージを軽減する
}
public void ぬすむ() {
// モンスターの持つ宝を盗む。失敗することもある
}
・
・
・
}
public class 黒魔道士 {
public void たたかう() {
// 装備している武器で攻撃する
}
public void ぼうぎょ() {
// 身を守り、ダメージを軽減する
}
public void 黒魔法() {
// 黒魔法を使用する
}
・
・
・
}
各ジョブを見てもらうと気付くと思いますが、
「たたかう」や「ぼうぎょ」などのメソッドはどのジョブでも使用可能なメソッドで、
その内容も一致しています。
これを大量にあるすべてのジョブに実装していくのは面倒ですね。
それどころか、ゲームの仕様が変わって「たたかう」が「こうげき」に変わったらどうでしょう?
すべてのジョブのすべてのメソッドを修正しないといけません。
そこでJavaには継承という機能が用意されています。
各ジョブの共通部分をまとめた"ジョブ"というクラスを作ってみます。
public class ジョブ {
public int lv; // キャラクターのレベル
public int hp; // キャラクターのヒットポイント
public int mp; // キャラクターのマジックポイント
public ジョブ() {
// 初期値としてlv=1,hp=50,mp=5を設定し、引数付きコンストラクタを実行
this(1, 50, 5);
}
public ジョブ(int lv, int hp, int mp) {
this.lv = lv;
this.hp = hp;
this.mp = mp;
}
public void たたかう() {
// 装備している武器で攻撃する
}
public void ぼうぎょ() {
// 身を守り、ダメージを軽減する
}
public void チェンジ() {
// 隊列を変更する
}
public void アイテム() {
// アイテムを使用する
}
}
見慣れない部分があると思います。
まず3~5行目の変数はメンバ変数(クラス変数)といい、
クラス内のどのメソッド、コンストラクタでも共通で使用できる変数です。
この例ではRPGに必須のレベルやHPをメンバ変数にしてみました。
次に7行目と12行目から始まるメソッド。これがコンストラクタです。
コンストラクタはクラスをインスタンス化(newする)際に呼ばれるメソッドで、
フィールドの初期化などを主に行います。
コンストラクタのメソッド名はクラス名とし、戻り値はなし(型も記述しない)です。
[修飾子] クラス名 (引数,・・・) { }
コンストラクタは複数設定可能で、7行目は引数なしのコンストラクタ。
12行目は引数付きのコンストラクタです。
実際にインスタンス化すると次のような感じです。
public class TestFF5 {
public static void main (String[] args){
// LV.1、HP50、MP5のジョブクラスのインスタンスsirocoが生成されます
ジョブ siroco = new ジョブ();
// LV.20、HP500、MP200のジョブクラスのインスタンスkururuが生成されます
ジョブ kururu = new ジョブ(20, 500, 200);
}
}
イメージが湧いたでしょうか??
因みにジョブクラスの引数なしコンストラクタのところで
"this(1, 50, 5)"とありますが、thisというのは自らのことで、
ここでは引数付きのコンストラクタを示します。
つまり"ジョブ(1, 50, 5)"と同意です。
また、引数付きのコンストラクタの中で"this.lv"などここでもthisを使用していますが、
これはコンストラクタの引数のlvと区別するために付けています。
(クラスのフィールドは本来はすべて"this."をつけれるが、不要な時は省略できます)
さて、ここからが本題。
各ジョブの共通部分をまとめたジョブクラスが出来たので、
それを継承して各クラスを作ってみましょう。
public class ナイト extends ジョブ {
public ナイト() {
super.hp = super.hp * 1.2;
super.mp = super.mp * 0.6;
}
public void 両手持ち() {
// 武器を両手で持ち、攻撃力を倍増させる
}
・
・
・
}
public class シーフ extends ジョブ {
public void ぬすむ() {
// モンスターの持つ宝を盗む。失敗することもある
}
・
・
・
}
public class 黒魔道士 extends ジョブ {
public void 黒魔法() {
// 黒魔法を使用する
}
・
・
・
}
public class TestFF5 {
public static void main (String[] args){
// シーフのインスタンスsirocoが生成されます。
// このとき、スーパークラスであるジョブクラスの引数なしコンストラクタが実行され、
// 各メンバ変数ははLV.1、HP50、MP5となる
シーフ siroco = new シーフ();
// スーパークラスであるジョブクラスのたたかうメソッドが実行される
siroco.たたかう();
// 黒魔道士クラスのインスタンスkururuが生成されます。
// このとき、スーパークラスであるジョブクラスの引数付きコンストラクタが実行され、
// 各メンバ変数はLV.20、HP500、MP200となる
黒魔道士 kururu = new 黒魔道士(20, 500, 200);
// 黒魔道士クラスの黒魔法メソッドが実行される
kururu.黒魔法();
// ナイトクラスのインスタンスrenaが生成されます。
// このとき、スーパークラスであるジョブクラスの引数なしコンストラクタが
// 実行され、メンバ変数はLV.1、HP50、MP5となります。
// そのあとナイトクラスのコンストラクタが実行され、
// ジョブクラスのhpを1.2倍、mpを0.6倍にして代入しています。
// つまりrenaの状態はLV.1、HP60、MP6となります
ナイト rena = new ナイト();
}
}
クラスを継承するには、クラス宣言の際に"extends 継承するクラス名"で行います。
継承したクラス(子クラス)は継承元のクラス(親クラス、スーパークラス)の
フィールドを使用できます。
これでどのジョブからでも「たたかう」や「ぼうぎょ」を使う事が出来ます。
また、共通部分の変更が発生しても、スーパークラスであるジョブクラスを
修正するだけで、すべてのクラスで修正後のメソッドを使用できます。
上の例でナイトクラスにだけコンストラクタを設定しています。
他のジョブについては記述していませんが、この場合は引数なしの
デフォルトコンストラクタが暗黙に宣言されています。
因みに、ナイトクラスのコンストラクタ内でスーパークラスのメンバ変数に
直接あたいを代入しているが、これはあまりよろしくありません。
メンバ変数をprivate宣言し、アクセッサメソッド(ゲッター・フッター)を使うほうが良いです。
今回は説明前なので、あえて直接代入とさせています。(そのほうがわかりやすいと思って)
インスタンス化の際のコンストラクタの実行順としては、
まずスーパークラスのコンストラクタが実行され、
そのあと子クラスのコンストラクタが実行されます。
長々と書いてきましたが、継承について少しは感じがつかめたでしょうか?
細かいことを書けば、大量の説明が必要になるので、書きませんが、
詳しく知りたくなった人は古本屋さんとかでJavaの本を手にとって見てはいかがでしょう??
次回は条件文について書こうと思います。
(ここは流石にFF5は無理かな~)