|
これまで色々とオブジェクト指向プログラミングを学んできましたが、Javaでは一貫して一つのキーワードがありました。それは
「抽象化」(Abstraction)
と言う概念です。 この抽象化とは何でしょうか?別な表現をすると。
「機能と実装の分離」
ということができます。これはJavaをオブジェクト指向言語として用いていると頻繁に出くわす構造です。例えば
「サンドイッチを食べる。」
という動作を例に考えてみましょう。抽象化の概念に従うとこれは
「サンドイッチ」と「食物を食べる」
というように分けられます。つまり最初の「サンドイッチを食べる」という具体的な動作から、より抽象的な「食物を食べる」という動作へと変更されています。これは定義が曖昧になった反面で、「サンドイッチ」を食べるという非常に限定的な動作が「食物」を食べるという汎用的な動作へと変化したことを意味します。この場合、機能は「食物を食べる」、実装は「サンドイッチを食べる」と解釈できます。そして抽象的、汎用的な動作として分離された「食物を食べる」という機能は、別な「パイナップルを食べる」という実装にも応用することができます。このように
目的の動作や目的を分析し汎用的な機能や動作を抽出するという行為
を抽象化といいます。
これをJavaにたとえるなら、抽象化されていない「サンドイッチを食べる」という動作は
eatSandwich( Sandwich sand )
というメソッドに相当するでしょう。しかしこれでは「パイナップルを食べる」という動作を実装するときに再び
eatPineapple( Pineapple pine )
と言うメソッドを追加しなければいけません。そのため「食物を食べる」という動作を抽出し
eatFood(Food food)
というメソッドを作ることにより食物であればなんでも食べられるようにすればあらゆるパターンに対応することができます。 ではこの時、どのようにして食べ物を統一的なFood型という型で扱えばよいのでしょうか?継承の概念を思い出した人は、SandwichもPineappleも、また今後増える予定の食物は全てFoodクラスを継承しておけばよいと考えるかもしれません。そのため、それでは解決できないように、以下のような意地悪な条件を追加しておきます。
パイナップルは既に「植物クラス」 を継承している
これでは、継承を利用しダウンキャストによりPineappleのインスタンスをFood型として扱うことができません。なぜならJavaにおいては一般にクラスの多重継承は許可されておらず、常に単一継承だからです。しかし「継承関係ではない、自分以外の型」を実現する術が一つだけありましたね?それがインターフェースです。上の「インターフェースとは?」や第7回の「コラム・多重継承」を繰り返しよく読んでみてください。Javaにおいて「型の遺伝」の側面での多重継承は禁止されていませんでした。つまりPineappleはインターフェースを用いることによりFood型としても扱うことができるのです。これにより「食物を食べる」という汎用的な動作はeatFoodメソッドにより実現することが可能となりました。
|
図:食物インターフェースの概念図
|
|
|
|
先ほどパイナップルは既に植物クラスを継承していると書きましたが、当然我々が食べている動物も動物クラスを継承しています。そして植物クラスを継承しているクラス、動物クラスを継承しているクラスの全てが食用になるわけではありません。例えば毒草クラスやドブ鼠クラスなどさまざまな理由で食用に適さないクラスが存在するわけです。そんな中で選別的に、しかも既存の継承関係に影響を及ぼさずに、食用のクラスは食物クラス型として扱うためにインターフェースを利用します。
|
これまでの要求と答えを整理すると以下のようになります。
要求:植物クラスを継承しているパイナップルクラスを食物(Food型)として扱いたい
答え:パイナップルの継承構造はそのままで、パイナップルは食物(Food型) をインターフェースとして取り込む
いかがでしょう?このように継承関係にないもの同士を一つのグループとして扱ってやる時(特に機能を汎用的にする時)にインターフェースは大きな威力を発揮します。ではインターフェースを用いて、人クラスが「食物を食べる」という動作をもち、色々な食物を食べるようなプログラムを作ってみてください。
サンプル用のソースファイル(JavaDoc)
|