インスタンスを生成しないクラス
ポリモーフィズムを使うことにはさまざまな利点があります。
例えば以下のような状況を考えてみましょう。
犬クラスと猫クラスがあり、それぞれ「鳴く」という機能bark( )メソッドがあります。
動物であることは一緒なのでAnimalクラスを作りました。
これでポリモーフィズムを用いて一緒に扱えます。
さっそく次のコードで確認してみましょう。
【インスタンスを生成しないクラス】
Animalクラス
public class Animal{ public void bark( ){ } }
Dogクラス
public class Dog extends Animal{ public void bark( ){ System.out.println("ワン"); } }
Catクラス
public class Cat extends Animal{ public void bark( ){ System.out.println("ニャー"); } }
Mainクラス
public class Main{ public static void main(String[ ] args){ Animal animals[ ] = new Animal[5]; animals[0] = new Dog( ); animals[1] = new Cat( ); animals[2] = new Cat( ); animals[3] = new Dog( ); animals[4] = new Cat( ); for(int i = 0; i < animals.length; i++){ animals[i].bark( ); } } }
実行結果
ワン ニャー ニャー ワン ニャー
オブジェクト指向という考え方は、現実世界のあらゆる「物」をプログラムで表現します。
今回のプログラムの場合、Animalクラスのインスタンスを生成していますが、Animalという姿をもった動物が現実にいるでしょうか?
「動物」とは、形のあるものではなく、抽象的なもののはずです。
つまり「犬でも猫でもなく、種類の決まっていない動物の実体(インスタンス)がある」ということはありえません。
それが鳴くこともないでしょう。
そこで、Animalクラスのbark( )メソッドをコメントにしてみます。
すると、このクラスを使おうとしたところでコンパイルエラーになってしまいました。
これは、
animals[i].bark( );
のanimals[i]はAnimalクラスとして宣言されているためです。
動的結合するので実行時にはnewしたインスタンスの型を使いますが、コンパイル時には宣言した型の情報を使います。
そのため、Animalクラスのbark( )メソッドはおそらく使うことはないにもかかわらず、削除することはできません。
オブジェクト指向ではこのように、クラスを設計していく段階で呼ばれることのない(存在だけしていればよい)メソッドや、インスタンスを生成することのないクラスが必然的に出てきます。
抽象メソッドと抽象クラス
「抽象クラス」とはその名の通り、抽象的なクラスを指します。
抽象クラスは、「インスタンスを生成することができないクラス」となります。
例えば、前項で登場したAnimalクラスのインスタンスを作ったときのことを考えてみましょう。
Animal obj = new Animal( ); obj.bark( );
これはまったく意味を持ちません。
(「動物が吠える」といっても、どう吠えたら良いのでしょう!?)
しかし、このようなコードを書くことができてしまいます。
こういった無意味なコードを書けないようにするのが「抽象メソッド」と「抽象クラス」なのです。
ポイント!抽象クラス
インスタンスを作ることのできないクラスです。
クラスの宣言をする際に「abstract」キーワードをつけると抽象クラスとなります。
public abstract class クラス名{ }
ポイント!抽象メソッド
実装(メソッドの中身)がないメソッドです。
オーバーライドされるために存在するメソッドとなります。
メソッドの宣言をする際に、“abstract”キーワードをつけると抽象メソッドになります。
その際、メソッドの中身は記述できません。最後に“;”が必要ですので注意しましょう。public abstract 戻り値 メソッド名( 引数リスト・・・・);
【抽象クラスと抽象メソッド】
先ほどのAnimalクラスを編集します。
public abstract class Animal{ public abstract void bark( ); }
抽象メソッドはメソッドの中身を書くことができません。
これは「鳴く機能がどこかにあります」という意味です。
1つ以上の抽象メソッドを持つクラスは、あるはずの機能がまだ実装されていないと判断されます。
そのため抽象クラスにする必要があり、インスタンス化できません。
ポイント!抽象メソッドを持つクラス
抽象メソッドを持つクラスは、抽象クラスにしないとコンパイルエラーが発生します。
「鳴く機能がどこかにあります」と宣言しているのに、実装しないまま、というのは禁止されているのです。
ですから、このクラスは継承してメソッドを実装(オーバーライド)しないと使うことができません。
ただし、サブクラスも抽象クラスにすれば実装していないメソッドが残っていてもコンパイルできます。
コンパイルができてもやはりインスタンスは生成できないので、どこかのサブクラスですべてのメソッドを実装しなければ使うことはできません。
特化と汎化
継承は、スーパークラスの機能をサブクラスから利用することができます。
しかし、これは1つの使い方にすぎません。
もう1つの使い方が抽象クラスの使い方です。
抽象クラスには抽象メソッドがあり(正確には、抽象メソッドのない抽象クラスも作れます)、機能がないためサブクラスで実装する必要があります。
継承をする動機は2種類あり、「特化」と「汎化」と言います。
ポイント!特化
特化とは、スーパークラスを拡張してさらに詳細な機能を追加することを言います。
もともと一般的なクラス(Humanなど)があり、それの特殊なケース(Salarymanなど)を追加するのが特化です。
ポイント!汎化
抽象クラスの考え方は汎化です。
実際に使うのは(DogやCatなど)具体的な実装を持つクラスですが、これらをまとめるためのクラスとして、より一般的なクラス(Animalなど)が必要になります。
上記2つは新しいクラスが必要だということが分かる順番が違います。
特化はもともとあったクラスに特別な機能を追加したいので、スーパークラスが先にあります。汎化は実装クラスを作ろうとしたところ、まとめるクラスが必要、ということが後から分かります。
先にDogクラスやCatクラスを実装しておいて、後からAnimalクラスを追加するのは手間がかかります。
そのため、アプリケーションを開発する際はいきなり実装を開始せず、これから作ろうとしているアプリケーションの全体像がどのようになっているのかしっかりと分析することが重要となります。
まとめ
抽象メソッドと抽象クラス
- 抽象メソッドはメソッドの内容を書くことができません。
- 抽象メソッドを持つクラスは抽象クラスにしなければなりません。
- 抽象クラスはインスタンスを生成できません。
特化と汎化
- 特化とは、すでにあるクラスを後から利用するタイプの継承を指します。
- 汎化とは、これから作るクラスに共通な機能を抽象クラスにまとめることを指します。
独学で挫折しそうになったら、オンラインプログラミングスクール