インタフェースは抽象メソッドと定数のみを持ち、究極的な抽象クラスのようなものと言える。インタフェースは「特定の機能」を表すが、実際の処理内容の詳細は規定しない。実際の処理内容は、インタフェースを実装するクラスに任されている。
※ ただし、Java 8 から、インタフェースには static メソッドとメソッドのデフォルト実装が定義できるようになった。
Javaでは、複数のクラスを継承して新しいクラスを作ること、多重継承、はできない。しかし、複数のインタフェースを実装することができる。
主として、抽象メソッドと定数から構成される。
インタフェースは class ではなく interface を使って指定する。書式は
interface インタフェース名 {
‥‥ 定数の宣言 ‥‥
‥‥ メソッドの宣言 ‥‥
}
である。抽象メソッドは
型名 メソッド名 ( パラメータの並び ) ;
の形で、本体を記述せずに定義する。
インタフェースの中のメソッドはすべて public で、デフォルト実装やstaticメソッドの場合を除き抽象メソッドである。public や abstract をつける必要はないが、明示しても構わない。
※ Java 8 以降では、インタフェースでメソッドのデフォルト実装とstaticメソッドが利用できるようになった。default キーワードをつけて
default 型名 メソッド名 ( パラメータの並び ) {
... デフォルトの処理 ...
}
の形で、デフォルト実装を持つメソッドを定義できる。また、
static 型名 メソッド名 ( パラメータの並び ) {
... 処理 ...
}
の形で、staticメソッドを定義できる。
定数は
型名 定数名 = 値 ;
の形で値を指定して定義する。
インタフェースの中で定義される変数はすべて public static final な定数である。public, static, final をつける必要はないが、明示しても構わない。全部明示する場合は、
public static final 型名 定数名 = 値 ;
のようになる。
機能を定義しており、実際のインスタンスを作れないという点でインタフェースは抽象クラスと似た点がある。しかし、以下の相違点があり、用途に応じて使い分ける必要がある。
| 抽象クラス | インタフェース | |
|---|---|---|
| コンストラクタ | 記述でき、サブクラスである具象クラスのインスタンス生成時に呼ばれる | 利用不可 |
| インスタンス変数 | 自由に持てる | 存在しない |
| static変数 | 自由に持てて値も変えられる | public finalな定数のみ。変数名のみでアクセス |
| メソッド | 自由に持てる。インスタンスメソッドの場合はインスタンス変数にアクセス可能 | すべてpublic。一般には定義のみの抽象メソッド。デフォルト実装とstaticメソッドはあるが、インスタンス変数は存在せず利用不可 |
インタフェースを使う側のクラスでは、クラスの宣言の際に implements を使ってインタフェースを指定する。こちらのクラスではインタフェースで宣言されているすべての抽象メソッドの中味を記述する必要がある。これをインタフェースの 実装 (implementation) と言う。
class クラス名 implements インタフェース名 {
‥‥ インタフェースの抽象メソッドの実装 ‥‥
}
上記クラスの中では、当然だが、自身で実装したメソッドを利用できる。
一方、インタフェースに含まれる定数は
定数名
だけを指定することで利用できる。一般のクラスの static 変数の場合だと
クラス名.変数名
の形で指定するが、インタフェースの定数はインタフェース名をつけずに指定することに注意。以下のようにカンマで区切って複数指定すれば、複数のインタフェースを実装できる。
class クラス名 implements インタフェース1, インタフェース2, インタフェース3 {
‥‥ インタフェース1の抽象メソッドの実装 ‥‥
‥‥ インタフェース2の抽象メソッドの実装 ‥‥
‥‥ インタフェース3の抽象メソッドの実装 ‥‥
}
アノテーションの利用
Java 6 以降では、メソッドの実装の際に
@Override
というアノテーション (注釈) を入れることが推奨されている。
メソッドを間違えて、実装あるいはオーバーライドできないメソッドに @Override を入れるとエラーになるので、プログラムの保守性が良くなる。
インタフェースには実体がないのでインスタンスを持つことはできない。しかし、インタフェースを型として指定することで、そのインタフェースを実装している任意のクラスのインスタンスを指定できる。つまり
インタフェース名 変数名;
は
class 任意のクラス implements インタフェース名 {
‥‥ 抽象メソッドの実装 ‥‥
}
任意のクラス 変数名;
に相当すると考えればよい。ただ、同等ではなく、インタフェース名を型とする変数では、そのインタフェースで定義されているメソッドしか呼び出すことができない。
(例)
インタフェース java.lang.Runnable (単に Runnable として利用できる) は run() というメソッドを持つインタフェースで、以下のように定義されていると考えられる。
package java.lang;
public interface Runnable {
public void run();
}
※ 上はあくまで想定コード。
この Runnable インタフェースを実装したクラスのインスタンスが必要なときは、例えば、下記の様に無名クラスの記法を使ってインタフェースを実装したクラスを定義して利用する。
インタフェース自身はインスタンス化できない。new Runnable() はインタフェース Runnable を実装した無名クラスのインスタンスを生成する。
Runnable r = new Runnable() {
@Override
public void run() {
for ( int i = 1; i <= 10; ++i ) {
System.out.print( " " + i );
}
System.out.println();
}
};
この Runnable を実装したインスタンス r は以下のように Thread (java.lang.Thread クラス) から利用できる。
new Thread( r ).start();
Thread クラスは Java の実行の単位 スレッド を表す。生成されたスレッドは start() メソッドにより実行が開始され、 Runnable を実装したインスタンス r の run() を実行し停止する。
上例では、スペース区切りの 1 から 10 までの数字と改行を標準出力に出力し停止する。
インタフェースを基にして新しいインタフェースを作ることができる。これをインタフェースの継承と言う。インタフェースの継承はクラスの継承と同じく extends というキーワードを使って基のインタフェースを指定する。
interface Ai {
‥‥‥
}
interface Bi extends Ai {
‥‥‥
}
新しく定義されたインタフェース Bi は基となったインタフェース Ai の サブインタフェース (subinterface) と言う。
また、基となったインタフェース Ai は新しく定義されたインタフェース Bi の スーパーインタフェース (superinterface) と言う。
インタフェース Bi を実装するクラスでは、インタフェース Bi で宣言されているメソッドを実装するだけでなく、スーパーインタフェース Ai で宣言されているメソッドもすべて実装する必要がある。
Javaでは、クラスでは多重継承はできないが、インタフェースは多重継承できる。その場合は extends の後にスーパーインタフェースをコンマで区切って並べる。
interface Ai {
‥‥‥
}
interface Bi {
‥‥‥
}
interface Ci {
‥‥‥
}
interface Di extends Ai, Bi, Ci {
‥‥‥
}
インタフェース Di を実装するクラスでは、インタフェース Di の中で宣言されているメソッドを実装するだけでなく、スーパーインタフェース Ai および Bi および Ci で宣言されているメソッドをすべて実装する必要がある。