|
|
抽象クラスとインターフェース |
H.Kamifuji . |
ここでは抽象クラスとインターフェースについて解説いたします。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
クラスを継承する時にスーパークラスで定義されたメソッドを、サブクラスにてオーバーライド(上書き)することが出来ますが、サブクラスで必ずオーバーライドされるようなメソッドもあります。 例えば全ての製品の基礎となるスーパークラスを作成し、個々の製品は必ずスーパークラスを継承したクラスを作成するとします。そして全てのサブクラスには製品名を表示するための「dispName」メソッドを定義することとしましょう。 class base{ public void dispName(){ /* 特に何もしない */ System.out.println("Alert:個別のクラスで再定義して下さい。"); } public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } } class seihinA extends base{ public void dispName(){ System.out.println("製品Aです"); } } class seihinB extends base{ public void dispName(){ System.out.println("製品Bです"); } }「dispName」メソッドは製品の名前を表示するメソッドですので、それぞれのクラスで別々の処理が必要となります。ただそれぞれのクラスで適当なメソッド名を使われると困りますのでスーパークラスでメソッドを用意することで、スーパークラスを継承するサブクラス全体でこのメソッド名を使うように推奨することが出来ます。 このようにサブクラスで用意して欲しいメソッドとしてスーパークラスでメソッドを用意はするけれども、スーパークラス自体では何の定義も必要無いようなメソッドの場合、メソッドの中身を書かずに定義することが可能です。 普通のメソッドは下記のようになっています。 メソッド名(引数, 引数, ...){ メソッドの中身 }これに対して中身が無いメソッドは下記のように記述します。 abstract メソッド名(引数, 引数, ...);通常はメソッドの中身を記述する"{"と"}"が無く、メソッド名の後に直ぐにセミコロン(;)を記述しています。つまりどんなメソッドなのかだけ記述してあり、中身が無いわけです。このようなメソッドを抽象メソッドといいます。 抽象メソッドはメソッド名の先頭に「abstract」を付けて区別します。 抽象メソッドを使って先ほどのサンプルを書き直すと下記のようになります。 abstract class base{ abstract public void dispName(); public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } } class seihinA extends base{ public void dispName(){ System.out.println("製品Aです"); } } class seihinB extends base{ public void dispName(){ System.out.println("製品Bです"); } }baseクラスの先頭にも「abstract」が付いています。これは抽象メソッドが1つでも含まれるクラスは、クラスそのものが抽象クラスとなるためです。つまり「中身が無いメソッドが少なくとも1つ含まれているクラス」と言うわけです。抽象クラスを定義する場合には、クラス名の先頭に「abstract」を付けます。 抽象メソッドと言うのは、メソッド名だけ定義された状態ですので、この抽象メソッドが含まれる抽象クラスを継承したサブクラスでは、必ずこのメソッドをオーバーライドして実際の中身を定義しなければいけません。その結果、サブクラスに必ず同じメソッド名の再定義を行わせることが出来るようになるわけです。 |
それでは実際に抽象クラスを具体的に使ってみましょう。 まず抽象クラスそのものからオブジェクトを作成してみます。 サンプルプログラム下記のサンプルを実行してみよう。class ctest18{ public static void main(String args[]){ base c = new base(); } } abstract class base{ abstract public void dispName(); public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } }上記のような抽象クラスからオブジェクトを作成するようなプログラムを作成しコンパイルすると下記のようになります。 [xxxxxxxx@dddddddddd Abstract]$ javac ctest18.java ctest18.java:3: エラー: baseはabstractです。インスタンスを生成することはできません base c = new base(); ^ エラー1個 [xxxxxxxx@dddddddddd Abstract]$エラーメッセージに出ているようにabstractクラスからオブジェクトを作成することは出来ません。abstractクラスと言うのは、クラスの中に少なくとも1つabstractメソッドが含まれています。abstractメソッドは中身が定義されていないメソッドですので、不完全なメソッドです。その為、abstractメソッドを含むabstractクラスからオブジェクトを作成することは出来ません。 abstractクラスは、継承して利用されることを前提したクラスであることに気をつけて下さい。 では次にabstractクラスを継承したクラスで、abstractメソッドを実装しなかった場合を試してみます。 サンプルプログラム下記のサンプルを実行してみよう。class ctest19{ public static void main(String args[]){ seihinA c = new seihinA(); } } abstract class base{ abstract public void dispName(); public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } } class seihinA extends base{ public void dispVersion(){ System.out.println("バージョンは1.0です"); } }今度はabstractクラスであるbaseクラスを継承したseihinAクラスを作成し、seihinAクラスのオブジェクトを作成しようとしています。seihinAクラスでは継承したbaseクラスに含まれているabstractメソッドのdispNameメソッドをオーバーライドしていません。 [xxxxxxxx@dddddddddd Abstract]$ javac ctest19.java ctest19.java:15: エラー: seihinAはabstractでなく、base内のabstractメソッドdispName()をオーバーライドしません class seihinA extends base{ ^ エラー1個 [xxxxxxxx@dddddddddd Abstract]$ 上記のエラーメッセージは次のことを表しています。抽象クラスを継承したクラスでは、継承した抽象クラスに含まれている抽象メソッドをオーバーライドするか、オーバーライドしない場合には継承したクラスもabstractクラスにするかどちらかにする必要があります。 つまりabstractクラスを継承した場合に、abstractメソッドをオーバーライドしなかった場合には、まだ未定義のメソッドが含まれたままになってしまいますので、継承したクラスもabstractクラスになってしまうわけです。その為、オーバーライドしない場合には次のように記述しておく必要があります。 abstract class base{ abstract public void dispName(); public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } } abstract class seihinA extends base{ public void dispVersion(){ System.out.println("バージョンは1.0です"); } }当然のことながら、seihinAクラスも抽象クラスですので、seihinAクラスからオブジェクトを作成することは出来ません。 最後にabstractクラスに含まれるabstractメソッドを継承したクラスで実装した例を見てみます。 サンプルプログラム下記のサンプルを実行してみよう。class ctest20{ public static void main(String args[]){ seihinA c = new seihinA(); c.dispName(); } } abstract class base{ abstract public void dispName(); public void dispCompany(){ System.out.println("XYZ社製造の製品です"); } } class seihinA extends base{ public void dispName(){ System.out.println("製品名はXXXです"); } public void dispVersion(){ System.out.println("バージョンは1.0です"); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Abstract]$ javac ctest20.java [xxxxxxxx@dddddddddd Abstract]$ java ctest20 製品名はXXXです [xxxxxxxx@dddddddddd Abstract]$抽象クラスはうまく使うことで、大規模なクラス設計を大人数で行う場合などに非常に有効な場合があります。 |
|