インターフェース
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
と、そのインターフェースを実装する複数のサブクラスで構成される。