Javaでは抽象クラスという通常のクラスと異なるクラスを作成することができます。
抽象クラスは継承されることを前提として作成するクラスです。抽象クラスでは、メソッド定義の際に抽象メソッドというメソッドを定義することができます。
抽象クラスは継承を前提とするクラスとなりますので、「抽象クラスを作成するかどうかはどのようなクラス設計を行うか」が関わってきます。
抽象クラスを定義するメリットや抽象クラスの使いどころなどが分かりにくい場合は、まずは文法だけを覚えるのも学習方法の一つです。
抽象クラスと抽象メソッド
重複になりますが、抽象クラスでは抽象メソッドを定義できます。抽象化とは意訳すると「あいまい」にすると思ってください。言い換えると、あいまいなクラスではあいまいなメソッドを定義することができます。
※抽象および抽象化という単語の正式な説明ではありません。単語の正式な意味は辞書などで確認してください。
抽象クラスは以下のようにabstractキーワードを付加して、クラスを定義します。
抽象クラス | abstract class クラス名{ |
---|---|
記述例 | abstract class AbstractTest{ |
抽象クラス内に定義する抽象メソッドは具体化されていない(あいまいな)メソッドです。なにが具体化されていないかというとメソッドの処理が具体化されていません。通常のメソッドでは処理を記述しますが、抽象メソッドでは処理を記述しません。
抽象メソッド | abstract 戻り値の型 メソッド名(引数) ; |
---|---|
記述例 | abstract double calc(double a, double b) ; |
通常のメソッドと違い、{}およびその中の処理を記述せず、代わりにabstractキーワードを付加します。
以下、通常のクラスと抽象クラスの違い
抽象クラスの特徴
抽象クラスには以下の特徴があります。
- クラスにはabstractを付加する
- 抽象メソッドを定義できる
- 抽象クラスからはインスタンス生成できない
- 抽象クラスを継承したサブクラスは抽象メソッドをオーバーライドする必要がある
抽象クラスは、処理の中身を持たない抽象メソッドを定義できます。(抽象メソッドが定義されていなくてもエラーにはなりません)メソッドは処理を行うための部品ですので、処理が書かれていない抽象メソッドだけでは、メソッドの役割に矛盾します。
そこで、抽象メソッドはオーバーライドを行い、サブクラス側(オーバーライドする側)で処理を記述します。
抽象クラスを継承するサブクラス側で、メソッド内の処理を記述することを実装と呼びます。
上の説明を要約すると以下のようになります。
1 | 抽象クラスは抽象メソッドを持つ |
2 | 抽象メソッドには処理を記述しない |
3 | [2の理由により]オーバーライドを利用して、処理はサブクラス側で定義する |
4 | [3の理由により]オーバーライドさせることが前提(継承させることが前提) |
5 | [抽象メソッドは処理がない為]抽象クラス自身はインスタンス生成できない |
「抽象クラスのインスタンス生成」や「抽象クラスを継承したサブクラスで抽象メソッドをオーバーライドしない」場合はコンパイルエラーとなります。
抽象クラスと抽象メソッド サンプルプログラム
以下のプログラム「AbstractLanguage.java」「JPtoEN.java」「Number35.java」という名前でworkフォルダ内に保存します。
保存が完了したら、コマンドプロンプトを起動し、Number35.javaをコンパイルおよび実行をしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//抽象クラス abstract class AbstractLanguage{ //変換に利用した言葉を保存するフィールド private String save = ""; //抽象メソッドを定義 abstract String convert(String t); //変換する言葉を登録するメソッド void save(String a, String b){ save += "[" + a + "]→[" + b + "]\n"; } //変換に利用した言葉を表示するメソッド void print(){ System.out.println("変換した単語は以下です"); System.out.println(save); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//抽象クラスを継承するサブクラス class JPtoEN extends AbstractLanguage{ //抽象メソッドをオーバーライド(実装) String convert(String jp){ String en ="変換できません"; if(jp.equals("こんにちは")){ en = "Hello"; save(jp,en); }else if(jp.equals("さようなら")){ en = "Goodbye"; save(jp,en); }else{ System.out.println(en); } return en; } } |
1 2 3 4 5 6 7 8 9 10 11 |
class Number35{ public static void main(String[] args){ JPtoEN jte = new JPtoEN(); jte.convert("こんにちは"); jte.convert("さようなら"); jte.print(); //コメントを解除するとコンパイルエラー //AbstractLanguage al = new AbstractLanguage(); } } |
実行例
C:\work>javac Number35.java
C:\work>java Number35
変換した単語は以下です
[こんにちは]→[Hello]
[さようなら]→[Goodbye]
※実行例はコンパイルおよび実行までの例を表示しています。
補足
Number35.javaのコメントを削除してコンパイルすると、以下のようなエラーとなります
Number35.java:9: エラー: AbstractLanguageはabstractです。インスタンスを生成することはできません
AbstractLanguage al = new AbstractLanguage();
^
エラー1個
AbstractLanguageとJPtoENの関係図
(いくつかのフィールドやメソッドは割愛しています)
補足 抽象クラスが利用される場面
前述したように抽象クラスは継承が前提の為、クラスの設計にも関わってきます。
抽象メソッドを作成後、サブクラスに継承し、かつオーバーライドを行って処理を記述するよりも、通常のメソッドを作成し処理を記述すればよいと感じるかもしれません。
しかし、抽象クラスはその特徴が設計時にそのままメリットとなります。
「メソッドのシグネチャ(戻り値、メソッド名、引数)はサブクラスでも同じだが、処理が決まっていない(もしくは処理がサブクラスごとに異なる)」という場合です。
例えば、サンプルのプログラムに新しいサブクラスJPtoITAを追加したとします。(以下の図参照)
抽象クラス側ではconvert()メソッドは「文字列をもらう→変換処理→別の文字列を戻り値とする」という流れだけを決め、変換処理の内容は記述しません。変換処理の内容は抽象クラスを継承した各サブクラス側のconvert()メソッドで定義するからです。
また、AさんとBさんが役割を分担し、「AさんがJPtoENクラス」「BさんがJPtoITAクラス」といったようなことも可能となります。
さらに、抽象クラスを継承させることにより、サブクラス側では抽象メソッドのオーバーライドは必須となりますので、「シグネチャは決まっているけど処理は各々で必ず実装してね」というメッセージを含めることもできます。
抽象クラス 復習問題
- 抽象クラスの特徴として間違っているものを選んでください。
- 解答群
- 抽象クラスを宣言する際はclassの左にabstractを記述する
- 抽象クラスを継承したサブクラスは抽象メソッドをオーバーライドする必要がある
- 抽象クラスからインスタンス生成を行うことはできない
- 抽象クラスを継承する際はimplementsキーワードを利用する
- 抽象クラスの特徴として間違っているものを選んでください。
- 解答群
- 抽象クラスは抽象メソッドを定義しなければコンパイルエラーとなる
- 抽象クラスを継承したサブクラス側で、抽象メソッドを全てオーバーライドする必要がある
- 抽象メソッドを定義する際、処理は記述しない
- 抽象クラスはabstractではないメソッドも定義できる
- 抽象クラス内で以下の記述をした際に、コンパイルエラーとならない記述を選んでください。
- 解答群
- abstract void method(int x, int y);
- abstract void method(int x){}
- void method(double x, double y){}
- int method(int a, int b);
- お疲れ様でした。
まとめ
- 抽象クラスは抽象メソッドを定義できる
- 抽象クラスおよび抽象メソッドはabstractキーワードを付加する
- 抽象メソッド定義時は処理を記述しない
- 抽象クラス自身はインスタンス生成できない
- 抽象クラスは継承させる為に作成する
- 抽象クラスを継承したサブクラスは抽象メソッドを全てオーバーライドする必要がある
- 抽象クラスを作成するかどうかはどのようにクラスやメソッドを設計するかが重要