今回やること
抽象クラスは、子クラスで共有する状態と処理を持ちながら、一部のメソッドを未実装で残せるクラスです。イベント名は共通に持ち、料金計算はチケット種別ごとに必ず実装させます。
前提: Java入門 #21 - 継承、#22 - ポリモーフィズム、#23 - インターフェースを終えていること。
Ticket(抽象クラス)
├── 共通: eventName、showEvent()
└── 未実装: price()
├── AdultTicketが実装
└── StudentTicketが実装
1. 共通部分を抽象クラスへ置く
abstract class Ticket {
private String eventName;
Ticket(String eventName) {
this.eventName = eventName;
}
void showEvent() {
System.out.println("イベント: " + eventName);
}
}
抽象クラスも、通常のフィールド、コンストラクタ、実装済みメソッドを持てます。ただしabstract classをnew Ticket(...)で直接作ることはできません。
2. 子に実装させるメソッドを宣言する
チケット種別によって料金が違うため、親では処理内容を決めず、抽象メソッドとして宣言します。
abstract int price();
抽象メソッドには本体の波括弧がなく、末尾を;にします。具体的な子クラスは、抽象メソッドを実装しない限りインスタンス化できません。
3. 子クラスで料金を実装する
class AdultTicket extends Ticket {
AdultTicket(String eventName) {
super(eventName);
}
@Override
int price() {
return 2000;
}
}
@Overrideを付けると、親のメソッド名や引数を間違えた時にコンパイラが知らせます。学生用も同じ契約で別料金を返します。
完成コードを動かす
AbstractClassDemo.javaとして保存します。
abstract class Ticket {
private String eventName;
Ticket(String eventName) {
this.eventName = eventName;
}
void showEvent() {
System.out.println("イベント: " + eventName);
}
abstract int price();
}
class AdultTicket extends Ticket {
AdultTicket(String eventName) {
super(eventName);
}
@Override
int price() {
return 2000;
}
}
class StudentTicket extends Ticket {
StudentTicket(String eventName) {
super(eventName);
}
@Override
int price() {
return 1200;
}
}
public class AbstractClassDemo {
public static void main(String[] args) {
Ticket adult = new AdultTicket("文化祭ライブ");
Ticket student = new StudentTicket("文化祭ライブ");
adult.showEvent();
System.out.println("一般: " + adult.price() + "円");
System.out.println("学生: " + student.price() + "円");
}
}
javac AbstractClassDemo.java
java AbstractClassDemo
変数の型をTicketにそろえても、実際のオブジェクトに応じたprice()が呼ばれます。これは前回までに学んだポリモーフィズムを、抽象メソッドの契約で保証した形です。
インターフェースとの役割の違い
この例で抽象クラスを使う理由は、eventNameという状態とコンストラクタ、showEvent()という共通実装を子へ渡したいからです。
| 欲しいもの | 選択の目安 |
|---|---|
| 密接な親子で状態・共通処理を共有 | 抽象クラス |
| 異なる種類へ同じ役割だけを約束 | インターフェース |
詳しいインターフェースの定義は#23へ戻って確認してください。ここでは両方を同時に実装しません。
成功確認
イベント: 文化祭ライブ
一般: 2000円
学生: 1200円
親のshowEvent()を再利用し、2つの子が異なるprice()を返せれば成功です。
よくあるつまずき
抽象クラスを直接newする
new Ticket(...)はできません。実装を完成させたAdultTicketなどを作ります。
子クラスで抽象メソッドを実装しない
具体クラスはすべての抽象メソッドを実装します。未実装なら、その子クラス自身にもabstractが必要です。
何でも抽象クラスへ集める
密接な親子関係と共有状態がある時に使います。役割だけを表したい場合はインターフェースを検討します。
練習
抽象クラスDeliveryに宛先とshowAddress()を持たせ、抽象メソッドfee()を宣言してください。BikeDeliveryとCarDeliveryで異なる料金を返します。
次のステップ
Java入門 #25 - staticとfinalで、クラス全体の共有値と再代入しない値を学びます。