継承
オブジェクト指向プログラミングの最も重要な機能の1つは、継承であるといっても良いでしょう。既存のクラスはスーパークラスと呼ばれ、スーパークラスの機能を引き継いで新規に生成されるクラスはサブクラスと呼ばれます。
継承はスーパークラスのメソッドと変数が新しいサブクラスにも引き継がれるものです。したがって、サブクラスではスーパークラスに無い属性や操作を実装すれば良いだけになりますのでソフトウェア開発の効率化となります。
あるクラスを継承して新たなクラスを作るには以下のように記述します。
修飾子 class クラス名 extends スーパークラス名
サブクラスは、スーパークラスの変数とメソッドを引き継ぐので、それらを宣言することなく使用することが出来ます。また直接のスーパークラスだけではなく、スーパークラスのスーパークラスのメンバも引き継ぐことができます。
又、スーパークラスで定義されていない変数やメソッドをサブクラスで宣言することもできる。更に、スーパークラスで定義されているメソッドの処理内容を変更し、同じ名前、引数、戻り値を持つメソッドとして再定義することが出来ます。これをオーバーライド(後述)と呼びます。
Phoneクラスの拡張
[ 前回作ったPhoneクラスを拡張してFAX電話を作る ]
public class FaxPhone extends Phone {
// FAXを発信する
public void outgoingFAX(String callNo){
System.out.println(usrName + "が" + callNo + "へファックスを送信します。");
}
// FAXを受信する
public void incomingFAX(){
System.out.println(usrName + "がファックスを受信しました。");
}
}
しかし上記のように記述しただけだとコンパイルエラーとなってしまいます。何故かというと、FaxPhoneクラスで使用している usrNameがスーパークラスで private 宣言してあるため、サブクラスから直接アクセスすることが出来ないのです。 このような場合は以下のように記述を変更すれば問題ありません。
- スーパークラスの privateを protectedへ変更する
- サブクラスでゲッターを使用して値を取得する
今回はスーパークラスの private を protectedへ変更してみます。
public class Phone{
protected String telNo;
protected String usrName;
〜 以下省略 〜
FaxPhoneクラスの使用
public class Example08_01 {
public static void main(String[] args){
// Phoneのインスタンスを生成する
FaxPhone faxphone1 = new FaxPhone();
// faxphone1に値をセット
// スーパークラスで実装)
faxphone1.setTelNo("03-xxxx-xxxx");
faxphone1.setUsrName("木村さん");
// 電話機能の使用
// スーパークラスで実装)
faxphone1.outgoingCall("090-xxxx-xxxx");
faxphone1.incomingCall();
// FAX機能の使用
// サブクラスで実装)
faxphone1.outgoingFAX("045-xxx-xxxx");
faxphone1.incomingFAX();
}
}
[ 実行結果 ]
木村さんが090-xxxx-xxxxへ電話を掛けます。
木村さんが着信しました。
木村さんが045-xxx-xxxxへファックスを送信します。
木村さんがファックスを受信しました。
privateから protectedへアクセス修飾子を変更したため、サブクラスから usrNameへアクセスできるようになりました。 またメソッドについても、サブクラスではスーパークラスに無い機能のみを実装すれば良いわけですからプログラミングの手間を大幅に減らすことが可能になります。
オーバーライド
オーバーライドとは、スーパークラスから継承したメソッドをサブクラスで再定義することです。オーバーライドを行なうためには以下の条件を満たす必要があります。 このオーバーライドによって Javaではオブジェクト指向で非常に重要な機能である ポリモーフィズム(多態性)を実現することが出来ます。
- スーパークラスのメソッドとシグネチャが同じであること
- スーパークラスのメソッドと戻り値型が同じであること
[オーバーライドサンプル]
[Animal.java]
public class Animal {
public void run(){ }
}
[Human.java]
public class Human extends Animal {
public void run(){
System.out.println("人間は二本足で走ります。");
}
}
[Cat.java]
public class Cat extends Animal {
public void run(){
System.out.println("猫は四本足で走ります。");
}
}
上記では Animalクラスを Human、Catクラスで拡張しています。さらに Animalクラスで定義されている run() メソッドを二つのサブクラスで再定義しているのが分かると思います。 Animal(動物)には走るという機能があるということを仮定してこのクラスに run() メソッドを定義しています。
スーパークラスを継承した具体的なサブクラスでは、Humanクラスであれば「人間は二本足で走ります。」と、Catクラスであれば「猫は本本足で走ります。」とメソッドを再定義しています。先程条件で出ていた、引数も戻り値もスーパークラスと同じですね。このようにスーパークラスの機能をサブクラスで再定義することを オーバーライドと言います。
※ 本来上記のようなケースでは抽象メソッドを使用しますが、抽象メソッドについては後述します。
オーバーライドしたメソッドを実行する
オーバーライドしたメソッドの使用例を紹介します。
[Example_08_02.java]
public class Example_08_02 {
public static void main(String[] args){
Animal animal1 = new Human();
Animal animal2 = new Cat();
animal1.run();
animal2.run();
}
}
[実行結果]
人間は二本足で走ります。
猫は四本足で走ります。
サブクラスのインスタンスはスーパークラス型の変数に代入可能です。 Animal型の変数に対して run() メソッドを実行していますが、出力結果を見ると実際に実行されたのはサブクラスのメソッドになります。
使用する側から見れば、「Animalは 走るという操作を持っている」ということさえ分かっていればインスタンス(実態)が人間であっても、猫であっても「走るという操作」を実行するだけで走らせることが可能になります。このように同一の定義で実装が別々にある性質を 多態性「ポリモーフィズム」と呼びます。
オーバーロード
先程オーバーライドという機能を説明しましたが、今度はオーバーロードという機能についてです。ちょっと呼び名が似ていますが、オーバーロードとは 同じ名称でことなるシグネチャを持つメソッドを定義することです。
たとえば、Animalクラスに以下のような eatと言うメソッドを追加したいとします。
・eatメソッドは引数を受け取らなければデフォルトで「ご飯を食べる」と出力する
・eatメソッドは引数を受け取ったとき、「(引数で受け取った文字列)を食べる」と出力する
オーバーロードが使えないプログラミング言語であれば「引数の値を判断してデフォルト値を出力するか否か判断する」か、「引数を受け取らない eatA、引数を受け取る eatBを用意する」等々の案が考えられます。
オーバーロードとは、このようなケースで引数を受け取らない eat() と、引数を受け取る eat(String food) を同じメソッド名で定義する事を言います。
[ オーバーロードの使用例 ]
public class Animal {
public void run(){ }
public void eat(){
System.out.println("ご飯を食べます。"):
}
public void eat(String food){
System.out.println(food + "を食べます。"):
}
}
このようにメソッド名は機能を表す名称が多いので、引数が異なるからといってメソッド名を変えていては分かりにくくなってしまいます。 メソッド名を変えることなくシグネチャの違う複数のメソッドを用意することが出来ます。
|