インターフェース
interfaceキーワードは、クラス階層内でインターフェースを定義するために使用される。interfaceは、以下の制限を除いてclassと非常に似ている:
- このキーワードで宣言される (実装はされない)メンバー関数は、
abstractキーワードがなくても抽象関数になる。 - 実装するメンバー関数は、
staticまたはfinalでなければならない。(staticおよびfinalメンバー関数については、後で説明する。) - そのメンバー変数は、
staticでなければならない。 - インターフェースはインターフェースのみを継承できる。
これらの制限にもかかわらず、クラスが継承できるinterfaceの数に制限はない。(対照的に、クラスは最大1つのclassを継承できる。)
定義
インターフェースは、クラスと同じようにinterfaceキーワードで定義される:
interfaceは、暗黙的に抽象であるメンバー関数を宣言するためのものだ。
そのインターフェースを継承するクラスは、インターフェースの抽象関数の実装を提供する必要がある。
インターフェース関数の宣言には、inおよびoutの契約ブロックを含めることができる。
契約継承の例は、後で構造体およびクラスの契約プログラミングの章で説明する。
インターフェースから継承するinterface
interfaceの継承構文は、classの継承構文と同じである:
インターフェースは多態性をサポートしている。インターフェースパラメータを取る関数は、オブジェクトの実際の型を知らなくても、そのパラメータを使用することができる。例えば、SoundEmitterパラメータを取る次の関数は、オブジェクトの実際の型を知らなくても、そのパラメータに対してemitSound()を呼び出す。
クラスと同様に、この関数は、SoundEmitterインターフェースを継承するあらゆる型のオブジェクトで呼び出すことができる。
各オブジェクトの特別なemitSound()関数が呼び出され、Violin.emitSoundおよびBell.emitSoundの出力が表示される。
♩♪♪
ding
複数のinterfaceから継承する
クラスは、最大1つのclassから継承できる。継承できるinterfaceの数に制限はない。
通信デバイスを表す次のインターフェースを考えてみよう:
Phoneクラスが、音の発生装置としても通信デバイスとしても使用される必要がある場合、そのクラスは両方のインターフェースを継承することができる:
この定義は、"電話は音の発生装置である"と"電話は通信デバイスである"という両方の関係を表現している。
このクラスのオブジェクトを構築するには、Phoneは両方のインターフェースの抽象関数を実装する必要がある。
クラスは、プログラムの設計に応じて、任意の数のインターフェースを継承できる。
interfaceから継承し、class
クラスは、最大1つのclassから継承することもできる:
AlarmClockは、Clockのメンバーを継承する。さらに、SoundEmitterインターフェースが要求するemitSound()関数も提供する。
interfaceを継承するinterface
別のインターフェースから継承されたインターフェースは、サブクラスが実装しなければならない関数の数を事実上増やす。
上記の定義によると、MusicalInstrumentになるためには、SoundEmitterが要求するemitSound()関数と、MusicalInstrumentが要求するadjustTuning()関数の両方を実装しなければならない。
例えば、ViolinがSoundEmitterではなくMusicalInstrumentを継承する場合、adjustTuning()も実装する必要がある。
staticメンバー関数
staticメンバー関数の説明は、前の章を短くするため、この章まで延期した。staticメンバー関数は、構造体、クラス、およびインターフェースで使用できる。
通常のメンバー関数は、常にオブジェクトに対して呼び出される。メンバー関数内で参照されるメンバー変数は、特定のオブジェクトのメンバーだ。
メンバーは、thisによっても参照できる:
staticメンバー関数はオブジェクトに対して動作しない。thisキーワードが参照するオブジェクトは存在しないため、thisはstatic関数内では無効だ。このため、通常のメンバー変数はstaticメンバー関数内では使用できない。
staticメンバー関数は、staticメンバー変数のみを使用できる。
構造体の章で前に見たPoint構造体を、今回はstaticメンバー関数を使って再設計しよう。次のコードでは、すべてのPointオブジェクトに、staticメンバー関数によって決定される一意のIDが割り当てられる。
static makeNewId()関数は、共通変数nextIdを使用できる。その結果、すべてのオブジェクトに一意のIDが割り当てられる。
0
1
2
上記の例にはstructが含まれているが、staticメンバー関数はクラスおよびインターフェースでも使用できる。
finalメンバー関数
finalメンバー関数の説明は、前の章を短くするため、この章まで延期した。finalメンバー関数は、構造体は継承をサポートしていないため、クラスとインターフェースにのみ関係する。
finalは、メンバー関数がサブクラスによって再定義できないことを指定する。ある意味で、このclassまたはinterfaceが提供する実装は、その関数の最終的な実装となる。この機能が役立つ例としては、アルゴリズムの一般的な手順をインターフェースで定義し、詳細をサブクラスに任せる場合が挙げられる。
Gameインターフェースを使って、この例を見てみよう。ゲームプレイの一般的な手順は、次のinterfaceのplay()関数によって決定されている。
サブクラスは、play()メンバー関数の定義を変更することはできない。サブクラスは、インターフェースで宣言されている5つの抽象メンバー関数の定義を提供することができる(そして提供しなければならない)。そうすることで、サブクラスはアルゴリズムの欠落しているステップを完了する。
上記の例にはinterfaceが含まれているが、finalメンバー関数はクラスでも使用できる。
使用方法
interfaceはよく使用される機能だ。ほぼすべてのクラス階層の頂点には、1つ以上のinterfaceがある。プログラムでよく見られる階層構造の1つは、単一のinterfaceと、そのインターフェースを実装する複数のクラスからなるものである:
MusicalInstrument
(インターフェイス)
/ | \ \
Violin Guitar Flute ...
実践ではより複雑な階層構造が存在するが、上記のシンプルな階層構造は多くの問題を解決する。
クラス階層の共通の実装詳細を中間クラスに移動することも一般的である。サブクラスはこれらの中間クラスから継承する。以下のStringInstrumentとWindInstrumentクラスには、それぞれのサブクラスの共通メンバーを含めることができる:
MusicalInstrument
(インターフェイス)
/ \
StringInstrument WindInstrument
/ | \ / | \
Violin Viola ... Flute Clarinet ...
サブクラスは、それぞれのメンバー関数の特別な定義を実装する。
抽象化
インターフェースは、プログラムの各部分を互いに独立させるのに役立つ。これを抽象化という。例えば、楽器を扱うプログラムは、楽器の実際の型を指定することなく、主にMusicalInstrumentインターフェースを使用して記述することができる。
Musicianクラスは、楽器の実際の型を知らなくても、MusicalInstrumentを含むことができる。
実際の楽器の型に関係なく、さまざまな型の楽器をコレクションに組み合わせることができる。
プログラムのほとんどの関数は、このインターフェースだけを使用して記述することができる。
プログラムの部品を互いに抽象化することで、プログラムの1つの部品を変更しても、他の部品を変更する必要がなくなる。プログラムの特定の部品の実装が特定のインターフェースの背後にある場合、そのインターフェースのみを使用するコードは影響を受けない。
例
次のプログラムは、SoundEmitter、MusicalInstrument、およびCommunicationDeviceインターフェースを定義している:
devicesはSoundEmitterスライスであるため、SoundEmitterを継承するあらゆる型のオブジェクト (つまり、SoundEmitterと"は"の関係にある型) を含むことができる。その結果、プログラムの出力は、さまざまな型のオブジェクトによって出力されるさまざまな音で構成される。
ding
♩♪♪
rrring
beep
要約
interfaceは、抽象関数のみで構成されるclassに似ている。interfaceは、staticメンバー変数と、staticまたはfinalメンバー関数を持つことができる。- クラスが構築可能であるためには、そのクラスが継承するすべてのインターフェースのすべてのメンバー関数の実装を持っている必要がある。
interfaceから継承できる数は無制限だ。- 一般的な階層構造は、単一の
interfaceと、そのインターフェースを実装する複数のサブクラスで構成される。