Javaプログラムは,関係しているクラスが共通の抽象スーパクラスを共有すること又はメソッドをObjectに追加することを不要とするために,インタフェースを使用することができる。
インタフェースは,複数の他のインタフェースの直接的拡張(direct extension)と宣言してよい。これによって,隠ぺいされるかもしれない定数以外の,インタフェースが拡張した元のインタフェース複数の他の抽象メソッド及び定数を,暗黙的に規定する。
クラスは,複数のインタフェースを直接的に実装する(directly implement)と宣言してもよい。これによって,そのクラスの任意のインスタンスはインタフェースによって規定されるすべての抽象メソッドを実装する。クラスは,必然的に,その直接的スーパクラス及び直接的スーパインタフェースのすべてのインタフェースを実装する。この(多重)インタフェース継承は,オブジェクトがいかなる実装も共有せずに(多重の)共通的な振舞いを実装することを可能とする。
宣言型をインタフェース型とする変数は,その値として,指定されたインタフェースを実装すると宣言されるクラスのインスタンスであるオブジェクト参照をもっていてよい。クラスがインタフェースのすべての抽象メソッドをたまたま実装しているのでは十分でない。そのクラス又はそのスーパクラスの一つは,インタフェースを実装するものと実際に宣言されていなければならない。そうでないときには,クラスはそのインタフェースを実装するとは考えない。
InterfaceDeclaration:
InterfaceModifiersopt interfaceIdentifier
ExtendsInterfacesopt InterfaceBody
インタフェースを名前付けするIdentifierが,同じパッケージ内の他のクラス又はインタフェースの名前として出現すれば,コンパイル時エラーが発生する。インタフェースを名前付けするIdentifierが,そのインタフェース宣言を含むコンパイル単位における単一型インポート宣言(7.5.1)経由で知られるクラス又はインタフェースの名前として出現すれば,コンパイル時エラーが発生する。次に例を示す。
class Point { int x, y; }
interface Point { void move(int dx, int dy); }
同じパッケージ内のclass及びinterfaceは,同じ名前をもてないので,コンパイル時エラーが発生する。
InterfaceModifiers:アクセス修飾子InterfaceModifierInterfaceModifiersInterfaceModifier InterfaceModifier: one ofpublic abstract
publicは,6.6で記述する。同じ修飾子が一つのインタフェース宣言で2回以上出現するとき,コンパイル時エラーが発生する。
abstractとする。この修飾子abstractは,もはやインタフェースに対しては意味がなく,新しいJavaプログラムでは使用しないことが望ましい。
extends節が提供されれば,宣言されるインタフェースは、他の名前付けされた各インタフェースを拡張し,したがって,他の名前付けされたインタフェースのメソッド及び定数を継承する。これらの名前付けされたインタフェースは,宣言されるインタフェースの直接的スーパインタフェース(direct superinterface)とする。宣言されたインタフェースを実装するクラスは,このインタフェースを拡張し,実装となるクラスにアクセス可能なすべてのインタフェースも実装する。
ExtendsInterfaces:
extends InterfaceType
ExtendsInterfaces , InterfaceType
次は,明確化のために4.3から再度記述する。
InterfaceType:
TypeName
インタフェース宣言のextends節内の各InterfaceTypeは,アクセス可能なインタフェース型を名前付けしなければならない。そうでないときには,コンパイル時エラーが発生する。
インタフェースが直接的又は間接的にそれ自体を拡張する循環が存在すれば,コンパイル時エラーが発生する。
インタフェースに対しては,Objectに類似するものは存在しない。すなわち,すべてのクラスはクラスObjectの拡張だが,すべてのインタフェースが拡張となる単一のインタフェースは存在しない。
スーパインタフェース(superinterface)関係は,直接的スーパインタフェース関係の推移的閉包(transitive closure)とする。次のいづれかが真ならば,インタフェースKはインタフェースIのスーパインタフェースとする。
KがIのスーパインタフェースであるときはいつでも,インタフェースIがインタフェースKのサブインタフェース(subinterface)と言う。
InterfaceBody:インタフェース型で宣言されるメンバの名前の有効範囲は,インタフェース型宣言の本体全体とする。{InterfaceMemberDeclarationsopt}InterfaceMemberDeclarations: InterfaceMemberDeclaration InterfaceMemberDeclarationsInterfaceMemberDeclaration InterfaceMemberDeclaration: ConstantDeclaration AbstractMethodDeclaration
publicとする。これらは,インタフェースもpublicと宣言されている,及びそのインタフェースを含むパッケージが7.1で記述されるとおりにアクセス可能ならば,そのインタフェースが宣言されているパッケージ外でアクセス可能とする。
インタフェースは,そのインタフェースが拡張するインタフェースから,そのインタフェースが隠ぺいするフィールド及び上書きするメソッドを除いたすべてのメンバを継承する。
ConstantDeclaration:
ConstantModifiers Type VariableDeclarator
ConstantModifiers: one of
public static final
インタフェース本体でのすべてのフィールド宣言は,暗黙的にpublic,static及びfinalとする。書式としてこれらのフィールド修飾子のどれか又はすべてを,冗長だが指定してもよい。ただし,記述しないことが望ましい。
インタフェースにおける定数宣言は,修飾子synchronized,transient又はvolatileのいずれも含んではならない。そうでないときには,コンパイル時エラーを引き起こす。
インタフェースが同じ名前の一つ以上のフィールド(8.3.3.3)を継承するのは可能とする。この状況は,それ自身ではコンパイル時エラーを引き起こさない。しかし,インタフェース本体内で単純名によっていずれかのフィールド参照しようとすると,参照があいまいなため,コンパイル時エラーが発生する。
同じフィールド宣言が,一つのインタフェースから複数の経路で継承されるかもしれない。この状況では,フィールドは1回だけ継承されると考えられ,単純名によってあいまい性なしに参照される。
インタフェースフィールドのための初期化式が単純名によって同じフィールド,又は同じインタフェース内で,ソーステキスト上,後方に宣言されている他のフィールドへの参照を含めば,コンパイル時エラーが発生する。そこで,次の式は,二つのコンパイル時エラーを引き起こす。
interface Test {
float f = j;
int j = 1;
int k = k+1;
}
これは,jが宣言される前にfの初期化で参照されること,及びkの初期化がkそのものを参照していることに基づく。
(ここで微妙なことは,コンパイル時に定数値で初期化されたフィールドが,実行時には最初に初期化される,ということである。これは,クラスのstatic
finalフィールド(8.3.2.1)にも適用される。特にこのことは,たとえ悪意のあるプログラムによっても,これらのフィールドが決してデフォルト初期値(4.5.4)をもたないことを意味する。詳細に関しては,12.4.2及び13.4.8を参照のこと。)
キーワードthis(15.7.2)又はキーワードsuper(15.10.2,15.11)がインタフェースのフィールドについての初期化式で出現すれば,コンパイル時エラーが発生する。
interface BaseColors {
int RED = 1, GREEN = 2, BLUE = 4;
}
interface RainbowColors extends BaseColors {
int YELLOW = 3, ORANGE = 5, INDIGO = 6, VIOLET = 7;
}
interface PrintColors extends BaseColors {
int YELLOW = 8, CYAN = 16, MAGENTA = 32;
}
interface LotsOfColors extends RainbowColors, PrintColors {
int FUCHSIA = 17, VERMILION = 43, CHARTREUSE = RED+90;
}
インタフェースLotsOfColorsは,YELLOWと名前づけられた二つのフィールドを継承する。インタフェースが単純名によってフィールドYELLOWへの参照を含まない限り,これは問題ない。(そのような参照は,フィールドに対する変数初期化子内で発生し得る。)
たとえインタフェースPrintColorsが値8ではなくて値3をYELLOWに与えても,インタフェースLotsOfColors内のフィールドYELLOWへの参照は,やはりあいまいさをもつ。
前節の例では,フィールドRED,GREEN及びBLUEがインタフェースLotsOfColorsで,インタフェースRainbowColors及びインタフェースPrintColorsを経由して複数回継承される。しかし,インタフェースLotsOfColors内でのフィールドREDへの参照は,フィールドREDの実際の宣言が1回だけなので,あいまいとは考えられない。
AbstractMethodDeclaration:
AbstractMethodModifiersopt ResultType MethodDeclarator Throwsopt ;
AbstractMethodModifiers:
AbstractMethodModifier
AbstractMethodModifiers AbstractMethodModifier
AbstractMethodModifier: one of
public abstract
アクセス修飾子publicは6.6で記述する。同じ修飾子が抽象メソッド宣言内に複数回現れれば,コンパイル時エラーが発生する。
インタフェース本体内でのすべてのメソッド宣言は,暗黙的にabstractとする。そこで,その本体は常にセミコロンで表現され,ブロックではない。Javaの旧バージョンとの互換性のために,書式上インタフェース内で宣言されたメソッドに対して冗長に,修飾子abstractを指定してもよい。ただし,記述しない方が望ましい。
インタフェース本体内でのすべてのメソッド宣言は,暗黙的にpublicとする。書式上インタフェースメソッドに対して冗長に修飾子publicを指定してもよい。ただし,記述しない方が望ましい。
インタフェースで宣言されたメソッドをstatic宣言してはならないことに注意すること。そうでないときには,コンパイル時エラーが発生する。これは,Javaにおいてstaticなメソッドがabstractであるはずがないからである。
インタフェースで宣言されたメソッドをnative宣言又はsynchronized宣言してはならないことに注意すること。そうでないときには,コンパイル時エラーが発生する。これは,それらのキーワードはインタフェースの属性ではなくて実装の属性を記述するからである。しかし,インタフェースで宣言されたメソッドを,インタフェースを実装するクラスでnative宣言又はsynchronized宣言されるメソッドで実装してもよい。
インタフェースで宣言されるメソッドをfinal宣言してはならないことに注意すること。そうでないときには,コンパイル時エラーが発生する。しかし,インタフェースで宣言されるメソッドを,インタフェースを実装するクラスでfinal宣言されるメソッドで実装してもよい。
インタフェース内でのメソッド宣言が,他のインタフェース内でのメソッドの宣言を上書きする場合,各々のメソッドで異なった返却値の型をもつ,又は一つが返却値の型をもち他方が void ならば,コンパイル時エラーが発生する。さらにメソッド宣言は,それが上書きするいかなるメソッド宣言とも矛盾するthrows節(8.4.4)をもってはならない。そうでないときには,コンパイル時エラーが発生する。
メソッドは,シグネチャ単位で上書きされる。例えば,もしインタフェースが同じ名前の二つのpublicなメソッドを宣言し,しかもサブインタフェースがそれらの一つを上書きすれば,そのサブインタフェースのもう一つのメソッドも継承する。
インタフェースは,インタフェース内での宣言によって上書きされないスーパインタフェースのすべてのメソッドを,その直接的スーパインタフェースから継承する。
インタフェースが,同じシグネチャ(8.4.2)の複数のメソッドを継承することは可能とする。この状況は,そのままではコンパイル時エラーを引き起こさない。インタフェースはすべてのメソッドを継承する。しかし,その継承された任意の二つのメソッドに対して,それらが違った返却値の型をもつか,又は一つが返却値の型をもち他方がvoidならば,コンパイル時エラーが発生する。(throws節は,この場合エラーを引き起こさない)。
同じメソッド宣言が一つのインタフェースから継承される経路は,複数存在することがある。この事実は,いかなる困難も引き起こさず,決してそのままではコンパイル時エラーを生じない。
throws節の間には,要求される関係は存在しない。
abstractで,いかなる実装も含まない。メソッドシグネチャを確定する以外に上書きメソッド宣言で達成できることは,メソッドの実装によって投げられるかもしれない例外を制限することがある。次に8.4.3.1で示す例の変形を示す。
class BufferEmpty extends Exception {
BufferEmpty() { super(); }
BufferEmpty(String s) { super(s); }
}
class BufferError extends Exception {
BufferError() { super(); }
BufferError(String s) { super(s); }
}
public interface Buffer {
char get() throws BufferEmpty, BufferError;
}
public interface InfiniteBuffer extends Buffer {
char get() throws BufferError; // override
}
interface PointInterface {
void move(int dx, int dy);
}
interface RealPointInterface extends PointInterface {
void move(float dx, float dy);
void move(double dx, double dy);
}
前述の例では,メソッド名moveがインタフェースRealPointInterfaceで三つの違ったシグネチャ(二つは宣言されており一つは継承されている)でオーバロードされている。インタフェースRealPointInterfaceを実装するいかなるクラスも,三つのメソッドシグネチャすべての実装を提供しなければならない。