2014年10月22日水曜日

- 継承 後編- Java

今回は「継承」の続き。
主に「抽象クラス」「インターフェース」について学ぶ。

内容は、そこまでは難しくない。
ただ、感覚が今までとは違う。

この、「抽象クラス」「インターフェース」は、「今」よりも「未来」に焦点をあてている。
今の自分のため!では、なく、第三者、未来の自分のために必要な道具である。

今はまだ、詳細が決まっていないメソッドがあるかもしれない。
そういうときに、役に立つのが、「継承」である。



抽象クラス

まず、継承の材料となるクラス(親クラス)を作る。
今までは、Catクラス、StrayCatクラス、BossStrayCatクラスなどを作ってきた。
Catクラスを使こともあったけれど、
今後は、「継承の材料となるクラス」は、newせずに使う(継承の材料のみの)クラスにしたい。

猫の話は、飽きてきただろうから、別のものを作る。

マリオブラザーズを作ることを考えてみます。(実際、ここでは作りません)
マリオがいて、敵が沢山いて。
敵が何をするかは、まだ考えていない。
けど、移動、攻撃、倒されるメソッドは必要だなー。

こういうとき、メソッドの中身を空にしておくという手もあるといえばある。
けれど、後で見返したときに、「実際に何もしない空のメソッド」なのか、「後から書き足す予定で空にしておいているメソッド」なのか、区別がつかない。
(コメントに書くという手もあるけれど、もし膨大な空のメソッドがあったら、大変ですし、他人や、未来の自分への負担が増える)

そこで、「抽象メソッド」というものを利用する。









この抽象メソッドの利用には、制約がある。

一つでも抽象メソッドを含むクラスは、「抽象クラス」というものにしなくてはいけない。
(抽象クラスになると、エラーが出る)












package blog;

public abstract  class Monster {

 // 以下、詳細未定メソッド
 public abstract void atack();

 public abstract void dying();

 public abstract void move ();
}


詳細未定メソッド atack,dying,move を定義した抽象クラス monster を書いた。

さらに、抽象クラスには制約がある。

抽象クラスは、newによるインスタンス化が禁止される!!
(newしようとすると、エラーが出る)

これが、上で書いた「抽象クラスはnewせずに使う、継承の材料のみのクラスにしたい」理由。
この制約により、何も知らない第三者が、継承の材料(と想定して作ったクラス)を誤ってnewすることもなくなる。

さらに、優れた機能がある。
まずは、この抽象クラスである monsterクラスを継承させた子クラスを作ってみる。
















上の画像は、 抽象クラスであるMonsterクラスを継承(extends)した Kuribouクラス を定義した際に現れたエラーメッセージです。

このエラーを消す方法は2つある

①Kuribouクラスの宣言の際に、 abstract をつける
②抽象メソッドを全てオーバーライドする

上に書いたようにabstract がついていたら、new出来ないため、
Kuribouクラスをインスタンス化させるためには、実質、全てオーバーライドする以外の方法はない。
なので、第三者、もしくは、未来の自分にオーバーライドを強制できる。



package blog;

public class Kuribou extends Monster {

 public void atack(){
  System.out.println("のんびり歩いて体をぶつけるよ!");
 }
 public void move(){
  System.out.println("左右にのんびり歩くよ!");
 }
 public void dying(){
  System.out.println("踏まれたらつぶれちゃうよ!");
 }
}
















オーバーライドによって、エラーメッセージは綺麗に消えました!


インターフェース

前回の継承で話したように、継承の階層が下がるにつれ、特化、あがるにつれ汎化する。
つまり、上流のクラスは、すべて抽象クラスになる。

特に抽象度が高いクラスは インターフェース にできる。
条件は

①すべてのメソッドが抽象メソッド
②基本的にフィールドを一つももたない。
(public static final がついたフィールドのみ許される。また、public static final は省略可能であり、フィールドを宣言すると自動的につく)











ここでも、ルールがあり、インターフェースとして宣言されたもののメソッドは、
自動的にpublic かつ abstract になる
(public abstract は省略可能。省略するのが一般的。)

さきほど、作った抽象クラス Monster は、全て抽象メソッド、フィールドも持たないという条件を満たしているので、インターフェースにしてみる。


package blog;

public interface Monster {

 // 以下、詳細未定メソッド
 void atack();

 void dying();

 void move ();
}


コードがスッキリしましたね。
しかし、ここで問題が出てきます。


























また、子クラスである Kuribouクラスにエラーメッセージが出てきました。
エラーメッセージに書いてあるように、
インターフェースを継承した子クラスを定義する際は、
extends ではなく implements を使う必要がある。
また、この行為は「実装する」と表現する。













package blog;

public class Kuribou implements Monster {

 public void atack(){
  System.out.println("のんびり歩いて体をぶつけるよ!");
 }
 public void move(){
  System.out.println("左右にのんびり歩くよ!");
 }
 public void dying(){
  System.out.println("踏まれたらつぶれちゃうよ!");
 }
}

extends の部分を implements に書き直して無事、エラーは消えた。


インターフェースって、そもそもなんだ??と思うけど、今は抽象クラスの親玉!!(あいまいなことしか言わない親玉)と思っておけば問題ない。
結局は、抽象クラスと継承の仕方は変わらず、Kuribouクラスでオーバーラードしなければnew出来ない。

つまり、
・このインターフェースを implements する複数の子クラスたちは、共通のメソッドを実装するように強制できる
・あるクラスがこのインターフェースを実装していれば、少なくとも、インターフェースが定めたメソッドは持っていることが保障される
(何度も言うが、オーバーライドしないと、new出来ないため!)

これだけみても、インターフェースと抽象クラスの違いがわからないと思う。
が、最大の強みがインターフェースには備わっている。

インターフェースにのみ許される多重継承

実は、Javaでは出来ないと思われていた(私が思っていただけですが!)多重継承が可能になる!

なぜ、多重継承がインターフェースにのみ許されているかというと……。
インターフェースをおさらいすると、抽象メソッドしかない。フィールドも持っていない。
つまり、内部実装がいっさい定められていない状態である。



















歩くモンスターインターフェース、飛ぶモンスターインターフェースを多重継承を利用して実装して
パタクリボー(空を飛ぶクリボーです)で、抽象メソッドである move() をオーバーライドする。
これは、なんの問題もない。

ダメな例(インターフェース以外から多重継承を試みる)は以下。


















歩くモンスタークラス、飛ぶモンスタークラス、どちらもメソッド move()があると、
実装の際に、どちらの動き方をするのかわからなくなってしまう。

そういう混乱を防ぐため、インターフェース以外からの多重継承は許されていない。
















package blog;

public  interface WalkMonster {

 void move();

}


package blog;

public  interface FlyingMonster {

 void move();

}


package blog;

public class PataKuribou implements FlyingMonster,WalkMonster {

 public void move (){
  System.out.println("飛んだり歩いたり!!");
 }

}


三つ以上のインターフェースを実装可能。

また、インターフェース同士の継承も可能。
その際は、implements ではなく、 extends と書く。
(クラス同士、インターフェース同士は extends)

クラスとインターフェースを両方使ったクラス定義も可能。













上手い例が思い浮かばなかったため、コードは書けない。
オーバーライドしつつ、インターフェースから継承した抽象メソッドを実装することができる。

0 件のコメント:

コメントを投稿