主に「抽象クラス」「インターフェース」について学ぶ。
内容は、そこまでは難しくない。
ただ、感覚が今までとは違う。
この、「抽象クラス」「インターフェース」は、「今」よりも「未来」に焦点をあてている。
今の自分のため!では、なく、第三者、未来の自分のために必要な道具である。
今はまだ、詳細が決まっていないメソッドがあるかもしれない。
そういうときに、役に立つのが、「継承」である。
■抽象クラス■
まず、継承の材料となるクラス(親クラス)を作る。
今までは、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 件のコメント:
コメントを投稿