Revised: Feb./09th/2003: Since: Dec./31st/2001
内容に抽象メソッドしか持たないクラスのようなものをインタフェースと呼びます。クラスと並んで、パッケージのメンバーとして存在します。インタフェースはクラスによって実装 (implements) され、実装クラスはインタフェースで宣言されている抽象メソッドを実装します。
インタフェースは、フィールドに定数を定義します。また、抽象メソッドのシグネチャを定義します。定数と抽象メソッドをリストしたものがインタフェースであり、クラスから実装されることによって使われます。クラスの場合は、単一の暮らすしか継承 (extends) できせんが、インタフェースの場合は、複数のインタフェースを実装 (implements) することができます。
class InterfaceImpl implements Interface1, interface2, interface3 {
...
}
インタフェースは、定数と抽象メソッドだけメンバーに持つことが出来ます。そもそも、オブジェクトの public インタフェースになるメソッドを宣言するためのものです。必要なのは、メソッド名、引数、戻り値の型だけです。
[修飾子] interface <インタフェース名> { データ型 変数名 = 値; 修飾子 戻り値のデータ型 メソッド名(引数の型宣言); }
interface
の修飾子は public
のみです。
インタフェースのメンバ変数は定数です。必ず値が代入されなければなりません。自動的に final public static
修飾子がつけられます。実装時に別の値を代入することは出来ません。他の修飾子は記述できません。
インタフェースのメソッドは抽象メソッドのみ記述可能です。自動的に abstract public
修飾子がつけられており、実装するクラス側で実装して完全なものにしておく必要があります。他の修飾子は記述できません。
interface Interface1 { // フィールド int INT_VAL1 = 10; // 抽象メソッド String method(int a, int b); } interface Interface2 { // フィールド int INT_VAL2 = 100; // 抽象メソッド void method(String s); }
クラスは任意の個数のインタフェースを実装できます。インタフェースを実装するクラスのことを実装クラスと呼びます。
[修飾子] class <クラス名> implements <インタフェース名リスト>{ メンバ変数 コンストラクタ 抽象メソッドの実装 普通のメソッド など }
インタフェースを実装したら、そこで定義されている抽象メソッドを全て実装しておかないとコンパイルエラーになります。
インタフェースの抽象メソッドは、自動的に public
修飾子がついていますので、実装時にも宣言しておく必要があります。
// 二つのインタフェースを実装するクラス class TestInterface implements Interface1, Interface2 { int x; // インタフェースの実装 public String method(int a, int b) { this.x = a + b; return "----Interface1----"; } // インタフェースの実装 public void method(String s) { Syste.out.println(s); } }
実装クラスは普通のクラスになるので、必要があれば、他のクラスを継承することも出来ます。
[修飾子] <クラス名> extends <スーパークラス名> implements <インターフェイ名スリスト>{ メンバ変数 コンストラクタ 抽象メソッドの実装 普通のメソッド など }
クラスの継承は一つのクラスからしか出来ませんが、インタフェースの実装は複数のインタフェースから出来ます。
インタフェースは他のインタフェースを継承することも出来ます。このとき、継承するインタフェースは複数あって構いません。クラスは単一継承ですが、インタフェースの場合は多重継承が許されます。
[修飾子] interface <インタフェース名> extends <スーパーインタフェース名のリスト>{ 内容 }
多重継承によって、インタフェースは網の目のような構造を構成しますが、その構造の中で変数は一意的でなければなりません。
多重継承でつながっているインタフェース内で、一つの変数名が複数箇所で定義されているとコンパイルエラーになります。
実装クラスでは、スーパーインタフェースも含めて全ての抽象メソッドを実装する必要があります。
インタフェースの多重継承の網の目構造の中で、メソッドが複数宣言されていても、実装クラスで必要なメソッドの実装は決まっているはずですから、その実装でオーバーライドするだけです。
インタフェースの特徴をまとめておきます。大事なことは、定数と抽象メソッドしか記述できないということです。
final public static
が付けられるabstract public
が付けられるimplements
) できるextends
) できるJava のようなオブジェクト指向言語では、データやメソッドの隠蔽によるカプセル化 (encapsulate) が重要です。データは全て private 修飾して外部から隠蔽し、public 修飾されたメソッドによってアクセスします。外部に公開する必要のない処理の実装も、private 修飾したメソッド内に記述することで、クラス外部からは隠蔽します。外部に対して、最低限度のメソッドだけを public 修飾して、公開するわけです。この、公開されたメソッドが、当該クラスをインスタンス化したオブジェクトに対して、別のオブジェクトがアクセス可能な全て、つまりインタフェースになるわけです。
オブジェクトの、外部に対するインタフェースとなるメソッドを宣言するのが、パッケージのメンバーである Java インタフェースです。
ここでは、メソッド内部の処理は記述しません。つまり、抽象メソッドだけを内部に持つことができます。インタフェースで宣言されたメソッドは、抽象メソッドなので、クラスで実装することで利用します。インタフェースを実装するクラスは、インタフェースの型をデータ型とする変数に代入互換であり、そこで宣言されている全てのメソッドを実装する義務を負います。
使う側からすれば、対象のオブジェクトの型となるクラスが実装しているインタフェースが分かれば、どんなメソッド名に何の引数を与えれば、どんな型の戻り値が得られるのかが分かるわけです。
抽象クラスには、抽象メソッドだけではなく、実装を持つメソッドも記述できました。その場合は継承されることで利用されますが、Java は単一継承なので、別のクラスを継承している場合は使えません。
一方、インタフェースは、継承 (extends) されるのではなく実装 (implements) され、複数のインタフェースをカンマ区切りリストで指定することができます。インタフェースは実装から型適合の仕組みを分離したものだといえます。
継承の多重継承が許されないのは、メソッド名だけでは、どのスーパークラスで実装されているものか区別できないからです。仮に、多重継承が許される場合に、複数のスーパークラスで、同じシグネチャのメソッドの実装が複数発見される場合、何れを選ぶか選択する仕組みが必要になります。単一継承ならば、継承階層の下から順番に探していき、最初に見つかった実装を採用するだけなので簡単です。
一方、インタフェースの多重実装が許されている理由は、重複するメソッド宣言の解決が不必要だからです。仮に複数のインタフェース間で同じメソッドが宣言されていても、それらは実装を持たないので、実装の競合は発生しません。そのメソッドの実装は、当該クラス内でなされているので、何れを選択するかが問題にならないのです。
実装クラスではインタフェースで宣言されているメソッドを全て実装する必要があります。実装していないメソッドが存在すれば、そのクラスは抽象クラスとして abstract
宣言しなければなりません。
インタフェースは、複数のクラスに実装されることを想定しています。このとき、インタフェース型の変数には、実装クラス型オブジェクトの参照を代入できます。同じインタフェース型の変数に対して、同じメソッドを呼び出しても、実行される実装は、そのオブジェクトがインスタンス化されたオリジナルのクラスでの実装によって、異なります。このような性質は、サブクラスとスーパークラスの場合にも成り立ちますが、インタフェースの場合は、多重実装が許されるので、この特徴が顕著だといえます。
インタフェースは、データ型として、クラスの仕様を定義するものであり、多態性のための言語仕様だといえます。