インタフェースと抽象クラスの違い

Javaを始めたばかりの新人からの質問です。両方とも継承するものです。両方とも変数の型として使えます。だから混乱するというのはよくわかります。理解の鍵は、インタフェースの継承と実装の継承は別々のものであるということです。インタフェースの継承は型の互換性を表すもので、クラスの継承は実装の再利用を表すものです。

たとえば、ペンギンというクラスは鳥というクラスを継承しているとします。ペンギンにはくちばしも羽もあるので、ほとんどは鳥の実装を再利用できます。しかし、普通の鳥は「空を飛べるもの」というインタフェースは実装できますが、ペンギンは空を飛べないのでこのインタフェースを実装できません。「空を飛べるもの」としては鳥だけでなく、飛行機や風船でもよいでしょう。

飛行機と風船の間の実装は、まったくといって共通点は見当たりません。ですから、これらをクラスとして実現した場合、両者の共通機能やデータを抽象クラスとして括り出すのはかなり無理があります。しかし、インタフェース上は両方とも飛ぶ()というメソッドを定義することは可能です。

インタフェースの継承は、クライアントから見た視点を提供します。インタフェースは、そのクラスがどのように実装されているかは関知しません。興味があるのはどのような型を実装しているかということだけです。オブジェクトは複数のインタフェースを実装することが可能です。飛行機は、「空を飛べるもの」だけでなく「エンジンを持つもの」というインタフェースを実装することができます。

一方、クラスの継承は、開発者からみた内部実装の構造の視点を提供します。クラスの継承はクラスの間の実装(データやメソッド)の再利用を可能にします。Javaでは多重継承をサポートしないので、同じクラスが複数のスーパークラスから継承できませんから、抽象クラスは同じクラス階層の中での実装の互換性だけしか表現できません。インコとニワトリのスーパークラスとして鳥という抽象クラスを定義できるるでしょうが、飛行機と風船の間で抽象クラスを定義するのは無理があります。

言語仕様としてインタフェースが存在しない場合を考えて見ましょう。C++のようなインタフェースのない言語では、飛行機と風船を同じようにあつかうためには、すべてのクラスがフレームワークレベルでObjectのような最上位のスーパークラスを継承する必要がありました*1。何か具体的な処理をするにはObjectを飛行機の型にキャストします。

このような上位クラスから下位クラスへのキャストはダウンキャストと呼ばれ、バグを誘引しやすいことが知られています*2Javaのインタフェースは、コンパイル時に型の互換性をチェックするので、飛行機と風船という実装の異なるクラスを安全に扱うことができます。

うーん。ここまで書いて終わりが見えなくなってきました。やはりポリモフィズムまで説明しないとダメかな。

*1:Templateがあるというツッコミはなしということで

*2:飛行機へのダウンキャストの例では、Objectの変数に飛行機ではなく風船が入っていたとしたら実行時エラーになります