Effective Javaの18章「抽象クラスよりインタフェースを選ぶ」を読んだので軽くまとめます。
インターフェースと抽象クラスの違い
インターフェースと抽象クラスは、複数の実装を許す為の仕組みの1つである。
両者の違いは、抽象クラスはメソッドの実装を持てるのに対してインターフェースは持てないことである。
インターフェースはクラス階層のどこに属していても実装できる。抽象クラスもどの階層においても実装できるが、Javaでは単一継承しか許されていない為、型定義をする為に抽象クラスを使用するのは非常に不便になる。
例えば、2つの異なるクラスA, Bに対して、抽象クラスCを実装させたいとする。その場合、AとBの祖先がCになるように実装しなければならない。ここで問題になるのは、AとBに関係ないクラスまでCを実装する必要が出て来てしまうことである。
ミックスイン
複数のインターフェースを混ぜ合わせて振る舞わせることをミックスインと呼ぶ。たとえば、以下のようなSingerインターフェースとSongwriterインターフェースがあるとする。
public interface Singer {
AudioClip sing(Song s);
}
public interface Songwriter {
Song compose(boolean hit);
}
これらをミックスインし、以下のようなSingerSongwriterインターフェースを定義できる。
public interface SingerSongwriter extends Singer, Songwriter {
AudioClip strum();
void actSensitive();
}
これは抽象クラスでは実現出来ない。なぜなら、Javaではクラスは2つ以上の親を持つこと(多重継承)ができないから。無理に継承を使って実装しようとすると、組み合わせ爆発(combinational explosion)が起こり、クラス階層が膨れ上がる。
骨格実装
Javaではインターフェースに実装を含むことは許されていないが、それ単体で提供しても実装者の負担になるだけである。そこで、インターフェースと一緒に骨格実装クラスを提供することでその問題を解決する。
骨格実装クラスはAbstractInterfaceと呼ばれ、Javaの主要なCollection Frameworkではそれが提供されている。例えば、 List インターフェースに対しては AbstractList が提供されている。
ほとんどの場合、骨格実装を拡張してインターフェースを実装することを選択すべきである。
骨格実装を作る場合、それが継承して使われることを想定して実装すべきである。ドキュメントと設計の指針に正しく従って設計を行う。
インターフェースの変更は困難
一度リリースされたpublicなインターフェースに対し、後から変更を加えることは非常に難しい。
このような問題を回避する為、正式リリース前にできるだけ多くのプログラマにこのインターフェースを使って実装してもらい、欠陥を早期に発見できるようにすると良い。インターフェースは最初から正しい設計をする必要がある。