今回はその続きです。
おさらいがてらジョブをクラスとして宣言していきます。
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は無理かな~)
0 件のコメント:
コメントを投稿