抽象クラスとは

今日は抽象クラスについて考えてみる。

抽象クラスとは、必ず継承して使わなければならないクラスのことである。ある抽象クラスAに属していて、Aのサブクラスに属していないようなオブジェクトはいない。

定義としてはただこれだけなのだが、一体何に使うのだろうか。

1つは置換可能性を保証するためである。リスコフの置換原則では、サブクラスはスーパークラスと置換可能でなければならないとする。

しかし、スーパークラスメソッドをオーバーライドしたときに置換可能性を満たせるのか。既に実装が存在して、その振る舞いを変えずに新しい実装に置き換えるなんてことは難しい。振る舞いを記述したものが実装だからだ。普通は実装を変えたら振る舞いも変わる。オーバライドする人は気を使わなければならない。

ならば、オーバーライドされることが分かっているメソッドは空で実装なんてなければよい。つまり、abstractメソッドにする。何もなければ、どんな実装に置き換えても振る舞いが変わったことにはならない。

抽象クラスは、abstractメソッドになっているところを任意に置き換えることができことを保証するわけだ。これによって、オブジェクトのメソッドを付け替えて多様な振る舞いをさせようという、いわゆるポリモーフィズムが実現できる。

f:id:fjkz:20160416171033p:plain


もう1つの抽象クラスの使い方は実装を共通化するためである。

これが、インターフェイスとの違いだ。インターフェイスは全てがabstractだが、抽象クラスは実装が持てる。

サブクラスで共通の実装を持つメソッドを抽象クラスで実装しておけば、サブクラス間で部品を共通化することができる。

例えば、Java標準ライブラリのAbstractMapクラスはMapインターフェイスを実装するための部品を共通化している。

f:id:fjkz:20160416152550p:plain

これ見ると、Mapインターフェイスは要るのかと疑問に思うけれども、この場合は要る。Mapインターフェイスの実装はAbstractMapのサブクラスだけではないからだ。Mapインターフェイスを実装するのに便利な部品として、AbstractMapが用意されているだけである。

ただ、部品を共通化する目的であれば、継承ツリーの中で頑張るのではなくて、共通部品を別のクラスに切り出した方が良い場合の方が多いのではと思っている。