2-4: Javaの多重継承とインターフェイス


・ 単純なクラスの継承の場合には、 直接のスーパークラスは1つしか存在しません。 しかし、 実際の財産の相続では複数の相手から財産を受け取る場合があり得ます。 また生物の進化の場合でも両親という複数の親から情報を受け継ぎます。 「複数のクラスをスーパークラスとして継承を行えないか?」 という考えが出てくるのもごく自然なことでしょう。 これを実現しようというのが「多重継承( multiple inheritance )」です。 これは非常に魅力的なアイディアです。 もし実現できれば、次のようなことが可能となるでしょう。

ただし多重継承はいくつか新しい問題を発生させます。 たとえば、 スーパークラスに選択された複数のクラスの中に 同じ名称の情報が含まれていた場合です。 新しいクラスは、 どちらから優先的に情報を受け継いだらいいのでしょうか?  なんらかのルールで優先順位を決めるにせよ、 そのような衝突が生じないように予防するにせよ、 言語は何か新しい仕組みを導入しなくてはいけないでしょう。


多重継承による混乱

また、多重継承によって継承関係が一挙に複雑化します。 単純な継承の場合にはクラス間の関係は、一方が他方のスーパークラスであるか、 サブクラスであるか、あるいは全く無関係である、この3通りの場合しかあり得ません。しかし、多重継承の場合には一筋縄ではいきません。 たとえば A, B, C という3つのクラスがあったとしましょう。 これらから、Aのみを継承したサブクラス、AとBのみを継承したサブクラス、 AとCのみを継承したサブクラス、・・・と全部で7種類のサブクラスが発生します。 これらのうち Aのみを継承したサブクラスと BとCのみを継承したサブクラスの 両者をスーパークラスとして新しいサブクラスを設計することも可能です。 ただし、Aのみを継承したサブクラスと AとBのみを継承したサブクラスの両者を スーパークラスとするのは、明らかに二重にスーパークラスを継承することになります。クラスの継承が何世代にも渡るとスーパークラスの数自体が膨大になりますから、 こうしたチェックだけでも大変です。
さらに豊富な機能を持つクラスを簡単に作り出せるという点にも、 実は落とし穴があることをつけ加えておきましょう。 オブジェクト指向の導入の意義は、 必ずしも目先の利益の追求ではなかったことを思い出してください。 手軽にソースコードが書けるということが アプリケーション開発の効率化を意味するとは必ずしも限らないのです。 むしろ大切なのはアプリケーションの保守の負担の軽減です。 あまりに安易に多重継承を繰り返してしまうと、 結果として中身が複雑でデバッグがしにくかったり、 バージョンアップや再利用が困難なクラスを生みだしかねません。
このように多重継承はプログラマにとっては大変魅力的な一方、 現実にはいろいろ難しい問題も含んでいることがわかりました。 では Javaには多重継承の機能は導入されているのでしょうか?  実は導入されているとも言えるし、そうでないとも言えるのです(?!)

・ Javaにおける多重継承を説明するためには、 「インターフェイス(interface)」の概念を避けて通ることはできません。 実はインターフェイスは全く新規に登場する概念ではありません。 それはある種の制限をつけられた特別なクラスです。 その制限とは次のようなものです。

要するに抽象クラスの考え方を徹底的に行ったものと考えればいいでしょう。 常識的に考えれば何も内容がないクラスをわざわざ設計するというのは 不思議な気がすると思います。 しかし抽象クラスのところで説明したように、 abstract宣言されたメソッドはあらかじめ機能を分離して記述しておくという 意味がありました。 インターフェイスは 「機能のみを完全に分離して記述されたクラス」ということができます。
なお、インターフェイスの定義を記述する場合、 文法的には通常のクラスと多少異なる取り扱いを受けます。 まずインターフェイスの定義の始まりを表すキーワードは、 class ではなく interface です。 もう一つ非常にささいな違いですが、 インターフェイスの定義の中ではメソッドが abstractであることは自明なため、 abstractの修飾子を省略してかまわないということがあります。 したがって、典型的なインターフェイスの定義は次のようになります。


public interface FirstInterface {
 
    public void firstProcess();
}
 

インターフェイスもクラスの継承の機能を利用できます。ただし、extends の後に 来るスーパークラスは、当然のことながらインターフェイスでなくてはなりません。


public interface SecondInterface extends FirstInterface {
 
    public void secondProcess();
}

ところで、このインターフェイスのソースコードをコンパイルすると いったいどうなるのでしょうか?  これは通常のクラスと変わりはありません。 FirstInterface.class, SecondInterface.class という名前のバイトコードが 生成されます。 (バイトコードの識別子はあくまで .classです。) Javaの仮想マシンにロードされる仕組みも全く同じです。 では、Javaがインターフェイスという概念を特に通常のクラスと区別して取り扱って いるのはなぜでしょうか?  これは Javaにおける多重継承の取り扱いの方針と大きく関係しています。

・ そろそろ「Javaには多重継承の機能があるのか?」 という疑問に答えを出しましょう。 実はこの問題に対する答は、 「多重継承の機能」をどこまで求めるかによって変わってきます。 もし、いくつかのオブジェクト指向言語で可能なように、 「任意の既存のクラスを複数組み合わせて継承することができる」 ことを多重継承の機能の条件だとするなら、答えは Noです。 Javaには上のような機能は備わっていません。 しかし、「あるクラスが複数のスーパークラスのグループに所属できる」ことを 多重継承の機能と見なすならば、答えは Yesになります。 その実現はインターフェイスによって行われます。
Javaのインターフェイスを通じた継承の具体例を見てみましょう。 Javaでの最も一般的な継承の記述は以下のような形式です。


public class SecondClass extends FirstClass
                   implements FirstInterface, SecondInterface ...{
 

implements という新しいキーワードが用いられています。 この後に継承すべきインターフェイスを指定します。 extendsの後には任意のクラス(インターフェイスでも可)が指定できます。 ただし、インターフェイスではないクラスは1つしか指定できません。 これに対して implementsの後にはインターフェイスしか指定できません。 ただし、複数のインターフェイスを指定することが可能です。 インターフェイスの中ではメソッドは全て abstract宣言されています。 したがってサブクラスがオブジェクトを生成できるクラスになるためには、 インターフェイスから受け継いだメソッドをすべて再定義して内容を用意する 必要が生じます。これは「クラスへのインターフェイスの実装」と言われます。 extends でスーパークラスに指定されたクラスも、 implementsでスーパークラスに指定されたクラスも、 どちらもスーパークラスとして取り扱われる点では全く同等です。