このドキュメントでは、Java SE 24の提案された機能である柔軟なコンストラクタ本体をサポートするために、Java言語仕様に加えられた変更について説明します。この機能の概要は、JEP 492を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2024-12-18: 15.9.2の小さなバグが修正されました。8.8.7.1への小規模な編集上の変更。
2024-12-05: 8.8.7.1の小さなバグが修正されました。
2024-11-01: 第3プレビューの初稿。次のものを含む小さなバグの修正のみ:
第6章: 名前
6.5 名前の意味の確認
6.5.6 式名の意味
6.5.6.1 単純な式名
式名が単一の識別子からなる場合は、その識別子が出現した時点でスコープ内にあるローカル変数、仮パラメータ、例外パラメータ、またはフィールドのいずれかを示す1つの宣言が存在する必要があります。そうでない場合、コンパイル時にエラーが発生します。
宣言でクラスCのインスタンス変数が示されている場合は(8.3.1.1)、次の両方すべてに当てはまる必要があります。そうでないと、コンパイル時にエラーが発生します。
式名が静的コンテキストに現れない(8.1.3)。
式名がC (8.8.7)の早期構築コンテキストに出現する場合、それは単純な割当て式(15.26)の左側のオペランドであり、名前付き変数の宣言にはイニシャライザがなく、単純な割当て式はCの早期構築コンテキストに含まれるラムダ式または内部クラス宣言に含まれていない。
式名は、Cのサブクラスの早期構築コンテキストに出現しない。
Cのネストされたクラスまたはインタフェースの宣言において式名が出現する場合、その式名の直接包含するクラスまたはインタフェースの宣言が、Cの内部クラスである。
たとえば、式名は、Cによって宣言された
static
メソッドの本体や、C内にネストされたstatic
クラスのインスタンス・メソッドの本体には出現できません。
宣言でローカル変数、仮パラメータまたは例外パラメータが示されている場合に、Xは、ローカル変数またはパラメータ宣言を含む、最も内側のメソッド宣言、コンストラクタ宣言、インスタンス・イニシャライザ、静的イニシャライザ、フィールド宣言または明示的なコンストラクタ呼出し文であるとします。Xで直接宣言されたローカル・クラス、ローカル・インタフェースまたは無名クラスDの本体に式名が直接的または間接的に現れる場合は、次の両方に当てはまる必要があり、そうでないとコンパイル時にエラーが発生します。
式名が静的コンテキストにない。
Dが内部クラスであり、式名の直接包含するクラスまたはインタフェースの宣言がD、またはDの内部クラスである。
たとえば、式名は、Dによって宣言された
static
メソッドの本体にも、(Dがローカル・インタフェースの場合) Dのデフォルト・メソッドの本体にも出現しないようにする必要があります。
宣言でfinal
でも事実上finalでもない(4.12.4)ローカル変数、仮パラメータまたは例外パラメータが示されている場合は、式名が、Xによって直接的または間接的に含まれる内部クラスに、またはXによって含まれるラムダ式(15.27)に出現すると、コンパイル時にエラーが発生します。
これらのルールの正味の効果は、(i)参照が静的コンテキストになく、(ii)参照から変数宣言への内部(
static
でない)クラスのチェーンがあり、(iii)変数がfinal
または事実上finalである場合に、ローカル変数、仮パラメータまたは例外パラメータを、そのスコープ内で宣言されたネストされたクラスまたはインタフェースからのみ参照できるということです。ラムダ式からの参照では、変数がfinal
または事実上finalである必要があります。
宣言により、単純な式の前に明確に割り当てられるfinal
変数が宣言される場合、この名前はその変数の値を意味します。そうでない場合、式名は、その宣言によって宣言された変数を意味します。
式名が割当てコンテキスト、呼出しコンテキスト、またはキャスト・コンテキストに出現する場合、式名の型はキャプチャ変換後のフィールド、ローカル変数、またはパラメータになります(5.1.10)。
そうでない場合、式名の型はフィールド、ローカル変数、またはパラメータの宣言された型になります。
つまり、式名が「右側」に出現する場合、その型はキャプチャ変換の対象になります。式名が「左側」に出現する変数である場合、その型はキャプチャ変換の対象にはなりません。
例6.5.6.1-1.単純な式名
class Test {
static int v;
static final int f = 3;
public static void main(String[] args) {
int i;
i = 1;
v = 2;
f = 33; // compile-time error
System.out.println(i + " " + v + " " + f);
}
}
このプログラムでは、i
、v
およびf
への割当てで左側として使用されている名前は、ローカル変数i
、フィールドv
およびf
の値(f
は変数final
であるため、変数f
ではありません)を示しています。したがって、この例では、最後の割当ての左側に変数がないため、コンパイル時にエラーが発生します。間違った割当てを削除すると、修正したコードをコンパイルできるようになり、次の出力が生成されます。
1 2 3
例6.5.6.1-2.インスタンス変数への参照
class Test {
static String a;
String b;
String concat1() {
return a + b;
}
static String concat2() {
return a + b; // compile-time error
}
int index() {
interface I {
class Matcher {
void check() {
if (a == null ||
b == null) { // compile-time error
throw new IllegalArgumentException();
}
}
int match(String s, String t) {
return s.indexOf(t);
}
}
}
I.Matcher matcher = new I.Matcher();
matcher.check();
return matcher.match(a, b);
}
}
フィールドa
およびb
はクラスTest
の本体全体でスコープ内にあります。ただし、concat2
メソッドの静的コンテキスト内、またはTest
の内部クラスではないネストされたクラスMatcher
の宣言におけるb
という名前の使用は無効です。
例6.5.6.1-3.ローカル変数および仮パラメータへの参照
class Test {
public static void main(String[] args) {
String first = args[0];
class Checker {
void checkWhitespace(int x) {
String arg = args[x];
if (!arg.trim().equals(arg)) {
throw new IllegalArgumentException();
}
}
static void checkFlag(int x) {
String arg = args[x]; // compile-time error
if (!arg.startsWith("-")) {
throw new IllegalArgumentException();
}
}
static void checkFirst() {
Runnable r = new Runnable() {
public void run() {
if (first == null) { // compile-time error
throw new IllegalArgumentException();
}
}
};
r.run();
}
}
final Checker c = new Checker();
c.checkFirst();
for (int i = 1; i < args.length; i++) {
Runnable r = () -> {
c.checkWhitespace(i); // compile-time error
c.checkFlag(i); // compile-time error
};
}
}
}
仮パラメータargs
は、メソッドmain
の本体全体でスコープ内にあります。args
は実質的にfinalであるため、名前args
をローカル・クラスChecker
のインスタンス・メソッドcheckWhitespace
で使用できます。ただし、名前args
をローカル・クラスChecker
のcheckFlag
メソッドの静的コンテキストで使用することは不正です。
ローカル変数first
は、メソッドmain
の本体の残りの部分のスコープ内にあります。first
も実質的にfinalです。ただし、checkFirst
で宣言された無名クラスはChecker
の内部クラスではないため、名前first
を無名クラス本体で使用することはできません。(ラムダ式は静的コンテキストにおいて出現するため、checkFirst
の本体におけるラムダ式でもfirst
を参照できません。)
ローカル変数c
は、メソッドmain
の本体の最後の数行のスコープ内にあり、final
と宣言されているため、名前c
をラムダ式の本体で使用できます。
ローカル変数i
はfor
ループ全体でスコープ内にあります。ただし、i
は実質的にfinalではないため、ラムダ式の本体でi
という名前を使用することは不正になります。
6.5.7 メソッド名の意味
6.5.7.1 単純なメソッド名
単純なメソッド名はメソッド呼出し式(15.12)のコンテキストに出現します。単純なメソッド名は、呼び出されるメソッドの名前を示す単一のUnqualifiedMethodIdentifierからなります。メソッド呼出しのルールでは、UnqualifiedMethodIdentifierで、メソッド呼出しの時点でスコープ内にあるメソッドが示されている必要があります。また、このルール(15.12.3)では、静的コンテキスト(8.1.3)において、またはインスタンス・メソッドを宣言するクラスまたはインタフェースの内部クラスでないネストされたクラスまたはインタフェースにおいて出現するインスタンス・メソッドへの参照は禁止されています。ルール(15.12.3)では、次のいずれかで出現するインスタンス・メソッドへの参照も禁止されています。
編集部: 前述の抹消線を引いた文で示されている、(15.12.3)にあるルールの概要は正しくありません。これは正しくは、次のことが禁止されているということです。
class T {
void foo() { ... }
static class Mid {
class L {
... foo() ... // Compile-time error as L is not an inner class of T
}
}
}
次のコードは(15.12.3)のルールでは許可されないということを示唆しています。
class B {
void foo() { ... }
}
class T extends B {
class Mid {
class L {
... foo() ... // No compile-time error even though L is not an inner class of B
}
}
}
例6.5.7.1-1.単純なメソッド名
次のプログラムは、呼び出すメソッドを決定する場合にスコープが果たす役割を示しています。
class Super {
void f2(String s) {}
void f3(String s) {}
void f3(int i1, int i2) {}
}
class Test {
void f1(int i) {}
void f2(int i) {}
void f3(int i) {}
void m() {
new Super() {
{
f1(0); // OK, resolves to Test.f1(int)
f2(0); // compile-time error
f3(0); // compile-time error
}
};
}
}
f1(0)
の呼出しでは、f1
という名前のメソッドのみがスコープ内になります。これはメソッドTest.f1(int)
であり、その宣言は匿名クラス宣言を含め、Test
本体全体でスコープ内になります。15.12.1では匿名クラス宣言にf1
という名前のメンバーがないため、クラスTest
での検索が選択されます。最終的にTest.f1(int)
が解決されます。
f2(0)
の呼出しでは、f2
という名前の2つのメソッドがスコープ内になります。まず、メソッドSuper.f2(String)
の宣言が匿名クラス宣言全体でスコープ内になります。次に、メソッドTest.f2(int)
の宣言が匿名クラス宣言を含め、Test
本体全体でスコープ内になります。(それぞれが宣言された時点では、もう一方はスコープ内にはないため、どちらの宣言ももう一方をシャドウ化することはありません。) 15.12.1ではf2
という名前のメンバーがあるためクラスSuper
での検索が選択されます。ただし、Super.f2(String)
はf2(0)
には適用できないため、コンパイル時にエラーが発生します。クラスTest
は検索されないことに注意してください。
f3(0)
の呼出しでは、f3
という名前の3つのメソッドがスコープ内になります。最初と2番目は、メソッドSuper.f3(String)
およびSuper.f3(int,int)
の宣言が匿名クラス宣言全体でスコープ内になります。3番目に、メソッドTest.f3(int)
の宣言が匿名クラス宣言を含め、Test
本体全体でスコープ内になります。15.12.1では、f3
という名前のメンバーがあるためクラスSuper
での検索が選択されます。ただし、Super.f3(String)
およびSuper.f3(int,int)
はf3(0)
には適用できないため、コンパイル時にエラーが発生します。クラスTest
は検索されないことに注意してください。
語彙的な包含スコープの前でネストされたクラスのスーパークラス階層の検索を選択することは、「コーム・ルール」と呼ばれます(15.12.1)。
第8章: クラス
クラス宣言では、新規クラスを定義し、その実装方法について記述します(8.1)。
最上位クラス(7.6)は、コンパイル・ユニットで直接宣言されたクラスです。
ネストしたクラスは、宣言が別のクラスまたはインタフェース宣言の本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.5、9.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。
ネストしたクラスの一部の種類は内部クラス(8.1.3)であり、これは、包含クラス・インスタンス、ローカル変数および型変数を参照できるクラスです。
ネストされたクラスは内部(8.1.3)である場合があります。その場合は、それで、(その宣言の出現位置に応じて)包含するクラスのインスタンス、ローカル変数および型変数を参照できることがあります。
enumクラス(8.9)は、名前付きクラス・インスタンスの小さいセットを定義する省略構文で宣言されたクラスです。
レコード・クラス(8.10)は、値の単純集計を定義する省略構文で宣言されたクラスです。
この章では、すべてのクラスに共通するセマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。
クラスがpublic
(8.1.1)として宣言される場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。
クラスはabstract
(8.1.1.1),として宣言でき、これが不完全に実装されている場合はabstract
として宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。クラスを拡張できる程度は、明示的に制御できます(8.1.1.2)。ここでは、サブクラスを制限するためにsealed
と宣言することも、サブクラスがないことを保証するためにfinal
と宣言することもできます。Object
を除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。
クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言しています。
クラス宣言は、他の種類の宣言と同じように、注釈(9.7)を使用して修飾できます。
クラスの本体は、メンバー(フィールド、メソッド、クラスおよびインタフェース)、インスタンスと静的イニシャライザ、およびコンストラクタ(8.1.7)を宣言します。メンバー(8.2)のスコープ(6.3)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子public
、protected
またはprivate
が含まれる場合があります(6.6)。クラスのメンバーには、宣言されたメンバーと継承されたメンバーの両方が含まれます(8.2)。新しく宣言されたフィールドは、スーパークラスまたはスーパーインタフェース内で宣言されたフィールドを隠すことができます。新しく宣言されたメンバー・クラスおよびメンバー・インタフェースは、スーパークラスまたはスーパーインタフェース内で宣言されたメンバー・クラスおよびメンバー・インタフェースを隠すことができます。新しく宣言されたメソッドは、スーパークラスまたはスーパーインタフェース内で宣言されたメソッドを隠す、実装する、またはオーバーライドすることができます。
フィールド宣言(8.3)では、1回インカネーションされるクラス変数、およびクラスのインスタンスごとに新しくインカネーションされるインスタンス変数を記述します。フィールドはfinal
(8.3.1.2)として宣言できます。この場合、このフィールドは1回のみ割り当てることができます。任意のフィールド宣言にイニシャライザを含めることができます。
メンバー・クラス宣言(8.5)では、前後のクラスのメンバーであるネストしたクラスを記述します。メンバー・クラスはstatic
である場合があります。この場合、メンバー・クラスは、前後のクラスのインスタンス変数にアクセスできません。または、内部クラスである場合もあります。
メンバー・インタフェース宣言(8.5)では、前後のクラスのメンバーであるネストしたインタフェースを記述します。
メソッド宣言(8.4)では、メソッド呼出し式(15.12)によって呼び出すことのできるコードを記述します。クラス・メソッドはクラスに関連して呼び出され、インスタンス・メソッドは、クラスのインスタンスである特定のオブジェクトに関連して呼び出されます。宣言が実装方法を示していないメソッドは、abstract
として宣言する必要があります。メソッドはfinal
(8.4.3.3)として宣言される場合があります。この場合、このメソッドは隠したりオーバーライドすることができません。メソッドは、プラットフォーム依存のnative
コード(8.4.3.4)によって実装される場合があります。synchronized
メソッド(8.4.3.6)は、synchronized
文(14.19)で使用されるように、本体を実行する前にオブジェクトを自動的にロックし、戻り時にオブジェクトを自動的にロック解除します。これにより、そのアクティビティを他のスレッド(17)のアクティビティと同期できるようになります。
メソッド名はオーバーロードする場合があります(8.4.9)。
インスタンス・イニシャライザ(8.6)は、作成時にインスタンスの初期化をサポートするために使用できる実行可能コードのブロックです(15.9)。
静的イニシャライザ(8.7)は、クラスの初期化をサポートするために使用できる実行可能コードのブロックです。
コンストラクタ(8.8)はメソッドと似ていますが、メソッド呼出しによって直接呼び出すことはできません。コンストラクタは、新しいクラス・インスタンスを初期化するために使用されます。メソッドと同様、これはオーバーロードする場合があります(8.8.8)。
8.1 クラス宣言
8.1.3 内部クラスと包含インスタンス
内部クラスは、明示的にも暗黙的にもstatic
でない、ネストされたクラスです。
内部クラスは次のいずれかです。
次のネストされたクラスは、暗黙的にstatic
であるため、内部クラスではありません。
ネストされたクラスに適用されるルールはすべて、内部クラスに適用されます。具体的に述べると、内部クラス自体がstatic
でない場合でも、内部クラスではstatic
メンバー(8.2)が宣言され継承され、静的イニシャライザ(8.7)が宣言される場合があります。
ネストされたインタフェースはすべて暗黙的に
static
(9.1.1.3)であるため、"内部インタフェース"はありません。
例8.1.3-1.内部クラス宣言とstaticメンバー
class HasStatic {
static int j = 100;
}
class Outer {
class Inner extends HasStatic {
static {
System.out.println("Hello from Outer.Inner");
}
static int x = 3;
static final int y = 4;
static void hello() {
System.out.println("Hello from Outer.Inner.hello");
}
static class VeryNestedButNotInner
extends NestedButNotInner {}
}
static class NestedButNotInner {
int z = Inner.x;
}
interface NeverInner {} // Implicitly static, so never inner
}
Java SE 16より前は、内部クラスでは静的イニシャライザを宣言できず、定数変数(4.12.4)であるstatic
メンバーのみを宣言できました。
構成要素(文、ローカル変数宣言文、ローカル・クラス宣言、ローカル・インタフェース宣言または式)は、最も内側にある場合、静的コンテキストで出現します。
メソッド宣言、
フィールド宣言、
コンストラクタ宣言、
インスタンス・イニシャライザ、または
静的イニシャライザ
、または明示的なコンストラクタ呼出し文
この構成要素を含むのは、次のいずれかです。
なお、コンストラクタ宣言またはインスタンス・イニシャライザにおいて出現する構成要素は、静的コンテキストにおいては出現しません。
静的コンテキストの目的は、
静的コンテキストを宣言で字句として包含しているクラスの現行インスタンスを明示的や暗黙的に参照することが禁じられているコードの区別です。この場合、静的コンテキストを宣言で字句として包含しているクラスの、定義されている現行インスタンスはありません。したがって、静的コンテキストにおいて出現するコードには、次のような制約があります。
フィールド・アクセス、メソッド呼出しおよびメソッド参照は、
super
で修飾できません(15.11.2、15.12.3、15.13.1)。字句として包含するクラスまたはインタフェースの宣言のインスタンス変数への非修飾参照は許可されません(6.5.6.1)。
字句として包含するクラスまたはインタフェースの宣言のインスタンス・メソッドへの非修飾呼出しは許可されません(15.12.3)。
字句として包含するクラスまたはインタフェースの宣言の型パラメータへの参照は許可されません(6.5.5.1)。
直接包含するクラスまたはインタフェースの宣言の外部にある、字句として包含するクラスまたはインタフェースの宣言のメソッドまたはコンストラクタによって宣言された型パラメータ、ローカル変数、仮パラメータおよび例外パラメータへの参照は許可されません(6.5.5.1、6.5.6.1)。
ローカル標準クラスの宣言(ローカル列挙クラスとは対照的)と無名クラスの宣言では、どちらでも、内部であるクラスが指定されますが、インスタンス化時には、直接包含するインスタンスはありません(15.9.2)。
内部メンバー・クラスをインスタンス化するクラス・インスタンス作成式は、修飾する必要があります(15.9.2)。
Oが直前と直後のCのクラスまたはインタフェース宣言で、Cの宣言が静的コンテキストに出現しない場合、内部クラスCはクラスまたはインタフェースOの直接の内部クラスです。
内部クラスがローカル・クラスまたは無名クラスである場合は静的コンテキストで宣言でき、その場合、包含クラスまたはインタフェースの内部クラスとみなされることはありません。
クラスCがクラスまたはインタフェースOの内部クラスとなるのは、それがOの直接内部クラスであるか、Oの内部クラスの内部クラスである場合です。
これは正常な状況ではありませんが、内部クラスの直前と直後のクラスまたはインタフェース宣言がインタフェースの場合に該当する場合があります。これは
default
またはstatic
メソッド本体においてクラスがローカルまたは無名クラスとして宣言された場合のみ発生します(9.4)。
クラスまたはインタフェースOは、それ自体の、ゼロ番目の、字句として包含するクラスまたはインタフェースの宣言です。
クラスOが、クラスCの、n番目の字句的に包含するクラスの宣言となるのは、それがCの、n-1番目の字句的に包含するクラスの宣言の、直接包含するクラスの宣言である場合です。
クラスまたはインタフェースOの直接内部クラスCのインスタンスiは、Oのインスタンスに関連付けられる場合があります(iの直接包含インスタンスと呼ばれる)。オブジェクトを直接包含するインスタンス(ある場合)は、オブジェクトの作成時に特定されます(15.9.2)。
オブジェクトoは、それ自体のゼロ番目の字句的な包含インスタンスです。
オブジェクトoがインスタンスiのn番目の字句的な包含インスタンスとなるのは、それがiのn-1番目の字句的な包含インスタンスを直接包含するインスタンスである場合です。
宣言が静的コンテキストにおいて出現する内部ローカル・クラスまたは無名クラスのインスタンスには、直接包含インスタンスはありません。また、ネストされたstatic
クラスのインスタンス(8.1.1.4)には、直接包含インスタンスはありません。
編集部: 既存のJLSでは、早期コンストラクタ・コンテキストと呼ばれるようになったものも静的コンテキストを使用することでカバーしています。これは、たとえば、明示的なコンストラクタ呼出しの引数として宣言された匿名クラスは静的コンテキスト内に示されるとみなされていることを意味します。次に例を示します。
class Outer {
int x;
class Inner extends Thread {
Inner() {
super(new Runnable() {
@Override
public void run() {
System.out.println("hello from " + Outer.this.x); // error??
}
});
}
}
}
既存のJLSでは、匿名クラスには直接包含するインスタンスがないため、参照のOuter.this.x
が有効かどうかは明確ではありません。(参照コンパイラは、このコードを受け入れます。)
早期構築コンテキストに示されるローカルおよび匿名のクラスは、常に直接包含するインスタンスを持つように定義されるようになりました。ルールでは、特定のクラスの早期構築コンテキストから参照できるものを指定します。前述の例では、匿名クラスはクラスOuter
の早期構築コンテキストにはないため、フィールドx
は参照できます。
それ自体がクラスまたはインタフェースSOの直接内部クラスであるCのそれぞれのスーパークラスSについては、Sに関してiを直接包含するインスタンスと呼ばれる、iに関連付けられたSOのインスタンスがあります。そのクラスの直接スーパークラス(存在する場合)に関わるオブジェクトの直接包含インスタンスは、明示的コンストラクタ呼出し文(8.8.7.1)を介してスーパークラス・コンストラクタが呼び出されるときに特定されます。
(宣言が静的コンテキストには出現しない)内部クラスが語彙的な包括クラスまたはインタフェース宣言のメンバーであるインスタンス変数を参照すると、対応する語彙的な包括インスタンスの変数が使用されます。
内部クラスに、字句として包含するクラスまたはインタフェースの宣言のメンバーであるインスタンス変数への有効な参照が含まれている場合は、対応する字句的に包含するインスタンスの変数が使用されます。
内部クラスで使用されているが宣言されていないローカル変数、仮パラメータまたは例外パラメータは、6.5.6.1で示されているとおり、final
または事実上final (4.12.4)である必要があります。
内部クラスで使用されるが、宣言されていないローカル変数は、内部クラスの本体より前で明確に割り当てる必要があります(16)。そうしないとコンパイル時にエラーが発生します。
変数の使用に関する同様のルールがラムダ式(15.27.2)の本体にも適用されます。
語彙的な包含クラスまたはインタフェース宣言の空のfinal
フィールド(4.12.4)は内部クラス内では割り当てることはできず、そうでない場合はコンパイル時にエラーが発生します。
例8.1.3-2.内部クラス宣言
class Outer {
int i = 100;
static void classMethod() {
final int l = 200;
class LocalInStaticContext {
int k = i; // Compile-time error
int m = l; // OK
}
}
void foo() {
class Local { // A local class
int j = i;
}
}
}
クラスLocalInStaticContext
の宣言は、静的メソッドclassMethod
内にあるため、静的コンテキストに出現します。クラスOuter
のインスタンス変数は、静的メソッドの本体内では使用できません。具体的には、Outer
のインスタンス変数をLocalInStaticContext
の本体内で使用することはできません。ただし、前後のメソッドからのローカル変数はエラーなしで参照できます(それらがfinal
として宣言されているか、実質的にfinalである場合)。
宣言が静的コンテキストには出現しない内部クラスは、包含クラス宣言のインスタンス変数を自由に参照できます。インスタンス変数は常にインスタンスに関して定義されます。包含クラス宣言のインスタンス変数の場合、インスタンス変数は内部クラスの包含インスタンスに関して定義される必要があります。たとえば、前述のクラスLocal
には、クラスOuter
の包含インスタンスがあります。さらに、次のような例があるとします。
class WithDeepNesting {
boolean toBe;
WithDeepNesting(boolean b) { toBe = b; }
class Nested {
boolean theQuestion;
class DeeplyNested {
DeeplyNested(){
theQuestion = toBe || !toBe;
}
}
}
}
ここでは、WithDeepNesting.Nested.DeeplyNested
のそれぞれのインスタンスに、クラスWithDeepNesting.Nested
の包含インスタンス(それを直接包含するインスタンス)とクラスWithDeepNesting
の包含インスタンス(その2番目の字句的な包含インスタンス)があります。
8.8 コンストラクタ宣言
8.8.7 コンストラクタ本体
コンストラクタ本体は、クラスの新しいインスタンスを作成するプロセス(12.5)の一部として実行されるコードのブロックです。コンストラクタ本体には、同じクラスの、または直接スーパークラスの別のコンストラクタの明示的な呼出し(8.8.7.1)が含まれている場合があります。
コンストラクタ本体の最初の文は、同じクラスの、または直接スーパークラスの別のコンストラクタの明示的な呼出し(8.8.7.1)である場合があります。
- ConstructorBody:
-
{
[ExplicitConstructorInvocation] [BlockStatements]}
-
{
[BlockStatements]}
-
{
[BlockStatements] ExplicitConstructorInvocation [BlockStatements]}
コンストラクタで、this
が関わる一連の1つ以上の明示的コンストラクタ呼出しによって直接的または間接的にそのコンストラクタ自体を呼び出すと、コンパイル時にエラーが発生します。
編集部: 上の段落は、8.8.7.1に移動しました
コンストラクタ本体に明示的コンストラクタ呼出しが含まれている場合は、その明示的コンストラクタ呼出しの前のBlockStatementsを、コンストラクタ本体のプロローグと呼びます。コンストラクタ本体のプロローグは空の場合があります。明示的コンストラクタ呼出しがないコンストラクタでのBlockStatementsと、コンストラクタ本体における明示的コンストラクタ呼出しの後のBlockStatementsを、エピローグと呼びます。コンストラクタ本体のエピローグも空の場合があります。
コンストラクト(文、ローカル変数宣言文、ローカル・クラス宣言、ローカル・インタフェース宣言または式)は、Cのコンストラクタ本体のプロローグに含まれている場合、またはCのコンストラクタ本体の明示的な呼出し(8.8.7.1)内にネストされている場合に、クラスCの早期構築コンテキスト内に示されます。
コンストラクタ本体が明示的なコンストラクタ呼出しを開始せず含まず、宣言されるコンストラクタが大もとのクラスObject
の一部でない場合、コンストラクタ本体には、(1)空のプロローグがあり、(2)スーパークラス・コンストラクタ呼出し"super();
" (引数をとらないそのその直接スーパークラスのコンストラクタの呼出し)で暗黙的に始まり、(3)コンストラクタ本体で指定された文(ある場合)は、コンストラクタ本体のエピローグを形成するために使用されます。
明示的または暗黙的コンストラクタ呼出しの可能性、値を明示的に返すことの禁止、return
文の禁止(14.17)を除き、コンストラクタの本体はメソッドの本体(8.4.7)と似ています。
なお、コンストラクタ本体には、最大でも1つの明示的コンストラクタ呼出しが含まれます。この構文では、たとえば、明示的コンストラクタ呼出しを
if
文の異なるブランチに配置することは不可能です。
return
文(14.17)は、それに式が含まれていない場合は、コンストラクタの本体で使用できます。
例8.8.7-1.コンストラクタ本体
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
static final int WHITE = 0, BLACK = 1;
int color;
ColoredPoint(int x, int y) {
this(x, y, WHITE);
}
ColoredPoint(int x, int y, int color) {
super(x, y);
this.color = color;
}
}
ここでは、最初のコンストラクタのColoredPoint
で、追加の引数を指定して2番目が呼び出されています。2番目のコンストラクタのColoredPoint
で、座標を渡してそのスーパークラスPoint
のコンストラクタが呼び出されています。
8.8.7.1 明示的なコンストラクタ呼出し
- ExplicitConstructorInvocation:
-
[TypeArguments]
this
(
[ArgumentList])
;
-
[TypeArguments]
super
(
[ArgumentList])
;
-
ExpressionName
.
[TypeArguments]super
(
[ArgumentList])
;
-
Primary
.
[TypeArguments]super
(
[ArgumentList])
;
- TypeArguments:
<
TypeArgumentList>
- ArgumentList:
- Expression {
,
Expression}
明示的コンストラクタ呼出し文呼出しは、次の2種類に分けられます。
代替コンストラクタ呼出しは、キーワード
this
で始まります(明示的な型引数が先頭に付くこともあります)。これらは、同じクラスの代替コンストラクタを呼び出す場合に使用します。スーパークラス・コンストラクタ呼出しは、キーワード
super
で始まるか(明示的な型引数が先頭に付くこともあります)、Primary式またはExpressionNameで始まります。これらは、直接スーパークラスのコンストラクタを呼び出す場合に使用します。これらは、さらに次のように分類されます。修飾されていないスーパークラス・コンストラクタ呼出しは、キーワード
super
で始まります(明示的な型引数が先頭に付くこともあります)。修飾されたスーパークラス・コンストラクタ呼出しは、Primary式またはExpressionNameで始まります。これらを使用すると、直接スーパークラスに関して、新しく作成されるオブジェクトを直接包含するインスタンス(8.1.3)をサブクラス・コンストラクタで明示的に指定できます。これは、スーパークラスが内部クラスである場合に必要になることがあります。
コンストラクタで一連の1つ以上の代替コンストラクタ呼出しによって直接的または間接的にそれ自体を呼び出すと、コンパイル時にエラーが発生します。
明示的コンストラクタ呼出し文では、現在のオブジェクトを参照する構成要素の使用を制限する、静的コンテキスト(8.1.3)が導入されます。特に、キーワードthis
およびsuper
は、静的コンテキスト(15.8.3、15.11.2)では禁止されています。これは、字句として囲んでいる宣言(6.5.5.1、6.5.6.1、15.12.3)のインスタンス変数、インスタンス・メソッドおよび型パラメータに対する参照が修飾されていないためです。
明示的なコンストラクタ呼出しでは、現在のオブジェクトを参照する構造体の使用を制限する、早期構築コンテキスト(8.8.7)が導入されます。特に、this
およびsuper
を使用する現在のオブジェクトへの参照は、インスタンス変数およびインスタンス・メソッド(6.5.6.1、6.5.7.1)への参照と同様に、早期構築コンテキスト(15.8.3、15.11.2)で制限されます。
TypeArgumentsがthis
またはsuper
の左側にある場合、いずれかの型引数がワイルドカードであれば(4.5.1)、コンパイル時にエラーが発生します。
Cはインスタンス化されるクラスであり、SはCの直接スーパークラスであるとします。
スーパークラス・コンストラクタ呼出し文が修飾されていない場合は、次のようになります。
Sが内部メンバー・クラスであるが、SがCを包含するクラスのメンバーでない場合は、コンパイル時にエラーが発生します。
そうでない場合は、OをSがメンバーである最も内側の包括クラスCにします。CはOの内部クラスである必要があり(8.1.3)、そうでない場合はコンパイル時にエラーが発生します。スーパークラス・コンストラクタ呼出しがクラスOの早期構築コンテキストに出現すると、コンパイル時にエラーが発生します。
Sが内部ローカル・クラスであり、Sが静的コンテキストに出現しない場合、Oは直前と直後のSのクラスまたはインタフェース宣言にします。CはOの内部クラスである必要があり、そうでない場合はコンパイル時にエラーが発生します。
- Sが静的コンテキストで宣言が出現するローカル・クラスの場合、Nを、Sの宣言を含む最も近い
static
メソッド宣言、static
フィールド宣言または静的イニシャライザにします。Nが、スーパークラス・コンストラクタ呼出しを含む最も近いstatic
メソッド宣言、static
フィールド宣言、または静的イニシャライザでない場合、コンパイル時にエラーが発生します。
編集部: 前述のアサーションは、JEP 395で導入された拡張機能のバグ修正です。
スーパークラス・コンストラクタ呼出し文が修飾されている場合は、次のようになります。
Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合は、コンパイル時にエラーが発生します。
また、pは
.super
の直前のPrimary式またはExpressionNameであり、OはSを直接包含するクラスであるとします。pの型がOまたはOのサブクラスでない場合、あるいはpの型にアクセスできない場合は(6.6)、コンパイル時にエラーが発生します。
iを、スーパークラス・コンストラクタ呼出しによって作成されるインスタンスにします。S (ある場合)に関してiを直接包含するインスタンスは次のように決定されます:
Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合、Sに関してiを直接包含するインスタンスは存在しません。
そうでない場合、スーパークラス・コンストラクタ呼出しが非修飾であれば、Sは内部ローカル・クラスまたは内部メンバー・クラスである必要があります。
Sが内部ローカル・クラスである場合、Oは、Sの、直接包含するクラスまたはインタフェースの宣言であるとします。
Sが内部メンバー・クラスである場合、Oは、SがメンバーであるCの、最も内側の、包含するクラスであるとします。スーパークラス・コンストラクタ呼出しがクラスOの早期構築コンテキストに出現すると、コンパイル時にエラーが発生します。
Oがn番目の語彙的なCの包含クラスまたはインタフェース宣言になるように、nを整数(n ≥ 1)にします。
Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。継承によってSがCのメンバーである場合もありますが、
this
のゼロ番目の字句的な包含インスタンス(つまり、this
自体)が、Sに関してiを直接包含するインスタンスとして使用されることはありません。それ以外の場合、スーパークラス・コンストラクタ呼出しが修飾されているとき、Sに関してiを直接包含するインスタンスは、Primary式またはExpressionNameの値であるオブジェクトです。
スーパークラス・コンストラクタ呼出しに即時包含インスタンスがある場合、このインスタンスはコンストラクタ呼出しの最初の実際の引数とみなされ、コンストラクタ呼出しに対する後続の実際の引数は、スーパークラス・コンストラクタ呼出しの引数リスト(ある場合)内の引数とみなされます(出現する順序で)。それ以外の場合、コンストラクタ呼出しの実際の引数は、スーパークラス・コンストラクタ呼出しの引数(ある場合)として、出現順に受け取られます。
明示的コンストラクタ呼出し文でスローできる例外型については、11.2.2で示します。
代替コンストラクタ呼出し文の評価では、まず、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。
スーパークラス・コンストラクタ呼出しの評価は、次のように進められます:
iは、作成されるインスタンスであるとします。S (ある場合)に関してiを直接包含するインスタンスを特定する必要があります。
Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合、Sに関してiを直接包含するインスタンスは存在しません。
そうでない場合、スーパークラス・コンストラクタ呼出しが非修飾であれば、Sは内部ローカル・クラスまたは内部メンバー・クラスである必要があります。
Sが内部ローカル・クラスである場合、Oは、Sの、直接包含するクラスまたはインタフェースの宣言であるとします。
Sが内部メンバー・クラスである場合、Oは、SがメンバーであるCの、最も内側の、包含するクラスであるとします。
Oがn番目の語彙的なCの包含クラスまたはインタフェース宣言になるように、nを整数(n ≥ 1)にします。
Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。継承によってSがCのメンバーである場合もありますが、
this
のゼロ番目の字句的な包含インスタンス(つまり、this
自体)が、Sに関してiを直接包含するインスタンスとして使用されることはありません。そうでない場合、スーパークラス・コンストラクタ呼出しが修飾されていれば、"
.super
"、pの直前にあるPrimary式またはExpressionNameが評価されます。pが
null
と評価されると、NullPointerException
が発生し、スーパークラス・コンストラクタ呼出しは突然完了します。それ以外の場合は、この評価の結果が、Sに関してiを直接包含するインスタンスです。
Sに関わるiの直接包含インスタンスが特定された後(存在する場合)、スーパークラス・コンストラクタ呼出し文の評価では、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。
最後に、スーパークラス・コンストラクタ呼出し文が正常に完了すると、Cのすべてのインスタンス変数イニシャライザ、およびCのすべてのインスタンス・イニシャライザが実行されます。インスタンス・イニシャライザまたはインスタンス変数イニシャライザIのテキストが別のインスタンス・イニシャライザまたはインスタンス変数イニシャライザJの前にある場合、IはJより先に実行されます。
スーパークラス・コンストラクタ呼出しが実際に明示的コンストラクタ呼出し文として出現するか、暗黙的に提供されるかに関係なく、インスタンス変数イニシャライザおよびインスタンス・イニシャライザの実行は実施されます。(代替コンストラクタ呼出しでは、こうした追加の暗黙的な実行は実施されません。)
編集部: スーパークラス・コンストラクタ呼出しの評価には、包含インスタンスの決定を含めないでください。これは、コンパイル時に明確に行われます。
まず、スーパークラス・コンストラクタ呼出しが修飾されている場合は、修飾するプライマリ式が評価されます。修飾プライマリ式が
null
と評価されると、NullPointerException
が発生し、スーパークラス・コンストラクタ呼出しは突然完了します。修飾された式が突然完了する場合、スーパークラス・コンストラクタ呼出しは同じ理由で突然完了します。コンストラクタ呼出しの実際の引数は、通常のメソッド呼出しと同様に左から右に評価され、コンストラクタが呼び出されます。
コンストラクタ呼出しの実際の最初の引数は、包含インスタンスである可能性があることに注意してください。
最後に、スーパークラス・コンストラクタ呼出しが正常に完了すると、Cのすべてのインスタンス変数イニシャライザとCのすべてのインスタンス・イニシャライザが実行されます。インスタンス・イニシャライザまたはインスタンス変数イニシャライザIのテキストが別のインスタンス・イニシャライザまたはインスタンス変数イニシャライザJの前にある場合、IはJより先に実行されます。
スーパークラス・コンストラクタ呼出しが実際に明示的なコンストラクタ呼出しとして出現するか、暗黙的に提供されるかに関係なく、インスタンス変数イニシャライザおよびインスタンス・イニシャライザは実行されます。(代替コンストラクタ呼出しでは、こうした追加の暗黙的な実行は実施されません。)
例8.8.7.1-1.明示的コンストラクタ呼出し文呼出しに関する制限事項
8.8.7の例にあるColoredPoint
という最初のコンストラクタが次のように変更されたとします。
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
static final int WHITE = 0, BLACK = 1;
int color;
ColoredPoint(int x, int y) {
this(x, y, color); // Changed to color from WHITE
}
ColoredPoint(int x, int y, int color) {
super(x, y);
this.color = color;
}
}
この場合、インスタンス変数color
を明示的コンストラクタ呼出し文で使用できないため、コンパイル時にエラーが発生します。
例8.8.7.1-2.修飾されたスーパークラス・コンストラクタ呼出し
次のコードでは、ChildOfInner
に語彙的な包含クラスまたはインタフェース宣言がないため、ChildOfInner
のインスタンスに包含インスタンスはありません。ただし、ChildOfInner
のスーパークラス(Inner
)には語彙的な包含クラス宣言(Outer
)があるため、Inner
のインスタンスにはOuter
の包含インスタンスが必要です。Outer
の包含インスタンスは、Inner
のインスタンスの作成時に設定されます。したがって、暗黙的にInner
のインスタンスである、ChildOfInner
のインスタンスを作成する際に、ChildOfInner
のコンストラクタ内で、修飾されたスーパークラス呼出し文を使用してOuter
の包含インスタンスを提供する必要があります。Outer
のインスタンスは、Inner
に関してChildOfInner
を直接包含するインスタンスと呼ばれます。
class Outer {
class Inner {}
}
class ChildOfInner extends Outer.Inner {
ChildOfInner() { (new Outer()).super(); }
}
意外に感じられますが、Outer
の同じインスタンスが、ChildOfInner
の複数のインスタンスについて、Inner
に関してChildOfInner
を直接包含するインスタンスとして機能することもあります。ChildOfInner
のこれらのインスタンスは、Outer
の同じインスタンスに暗黙的にリンクされます。次のプログラムでは、Outer
のインスタンスをChildOfInner
のコンストラクタ(修飾されたスーパークラス・コンストラクタ呼出し文でそのインスタンスを使用する)に渡すことによって、このことを実現します。明示的コンストラクタ呼出し文のルールでは、その文呼出しを含むコンストラクタの仮パラメータを使用することは禁止されていません。
class Outer {
int secret = 5;
class Inner {
int getSecret() { return secret; }
void setSecret(int s) { secret = s; }
}
}
class ChildOfInner extends Outer.Inner {
ChildOfInner(Outer x) { x.super(); }
}
public class Test {
public static void main(String[] args) {
Outer x = new Outer();
ChildOfInner a = new ChildOfInner(x);
ChildOfInner b = new ChildOfInner(x);
System.out.println(b.getSecret());
a.setSecret(6);
System.out.println(b.getSecret());
}
}
このプログラムでは、次の出力が生成されます。
5
6
その結果、ChildOfInner
の異なるインスタンスへの参照が従来の意味で別名でなくても、そのような参照を通じて、Outer
の共通インスタンス内のインスタンス変数の操作を見ることができます。
8.10 レコード・クラス
8.10.4 レコード・コンストラクタ宣言
レコード・クラスでは、そのレコード・コンポーネントが適切に初期化されるようにするために、デフォルトのコンストラクタ(8.8.9)は暗黙的に宣言されません。かわりに、レコード・クラスには、レコード・クラスのすべてのコンポーネント・フィールドを初期化する、明示的または暗黙的に宣言された、標準コンストラクタがあります。
レコード宣言で標準コンストラクタを明示的に宣言する方法としては、次の2つがあります。適切なシグネチャを使用して標準コンストラクタを宣言する方法か(8.10.4.1)、コンパクト・コンストラクタを宣言する方法です(8.10.4.2)。
正規として修飾される標準コンストラクタのシグネチャと、コンパクト・コンストラクタ用に導出されるシグネチャがあるとすると、コンストラクタ・シグネチャのルール(8.8.2)では、正規として修飾される標準コンストラクタとコンパクト・コンストラクタの両方がレコード宣言にある場合、コンパイル時にエラーが発生することになります。
どちらにしても、明示的に宣言された標準コンストラクタでは、次のように、少なくともレコード・クラスと同じくらいのアクセスを提供する必要があります。
レコード・クラスが
public
の場合、標準コンストラクタはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。レコード・クラスが
protected
の場合、標準コンストラクタはprotected
またはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。レコード・クラスにパッケージ・アクセスがある場合は、標準コンストラクタを
private
にしないでください。それをすると、コンパイル時にエラーが発生します。レコード・クラスが
private
の場合、標準コンストラクタはアクセス可能性で宣言できます。
明示的に宣言された標準コンストラクタは、固定個引数コンストラクタまたは可変個引数コンストラクタ(8.8.1)です。
レコード・クラスRの宣言で標準コンストラクタが明示的に宣言されない場合は、標準コンストラクタrが、次の特性で、Rで暗黙的に宣言されます。
rのシグネチャには、型パラメータがなく、次に明示する、Rの導出仮パラメータ・リストによって与えられた仮パラメータがあります。
rには、Rと同じアクセス修飾子があります(Rにアクセス修飾子がない場合を除きます。ない場合は、rにパッケージ・アクセスがあります)。
rには、
throws
句がありません。rの本体では、レコード・ヘッダーでのレコード・コンポーネント(コンポーネント・フィールドに対応する)の出現順序で、rの対応する仮パラメータを使用してレコード・クラスの各コンポーネント・フィールドが初期化されます。
レコード・クラスの導出仮パラメータ・リストは、次のように、レコード・ヘッダー内の各レコード・コンポーネントから仮パラメータを順番に導出することで形成されます。
レコード・コンポーネントが可変個引数レコード・コンポーネントでない場合、導出された仮パラメータの名前および宣言型は、レコード・コンポーネントと同じになります。
レコード・コンポーネントが可変個引数レコード・コンポーネントである場合、導出される仮パラメータは、名前および宣言型がレコード・コンポーネントと同じである、可変個引数パラメータ(8.4.1)です。
導出された仮パラメータに付けられる注釈は、存在する場合は、レコード・コンポーネント内に出現し、その注釈インタフェースは、仮パラメータ・コンテキストまたは型コンテキスト、あるいはこれら両方(9.7.4)において適用できます。
レコード宣言には、標準コンストラクタではないコンストラクタの宣言が含まれている場合があります。レコード宣言内のすべての非標準コンストラクタの本体には、最初に代替コンストラクタ呼出し(8.8.7.1)が含まれている必要があります。そうでないと、コンパイル時にエラーが発生します。
第11章: 例外
11.2コンパイル時の例外検査
11.2.2 文の例外分析と明示的なコンストラクタ呼出し
スロー式に静的型Eがある、finalまたは事実上finalの例外パラメータではないthrow
文(14.18)では、E、またはスロー式でスロー可能な例外クラスをスローできます。
たとえば、文
throw new java.io.FileNotFoundException();
ではjava.io.FileNotFoundException
のみをスローできます。正式には、java.io.FileNotFoundException
のサブクラスやスーパークラスを"スローできる"わけではありません。
スロー式がcatch
句Cのfinalまたは事実上finalの例外パラメータであるthrow
文では、次の場合に、例外クラスEをスローできます。
Eが、Cを宣言する
try
文のtry
ブロックでスローできる例外クラスである。またEに、Cの捕捉可能例外クラスのいずれかと代入の互換性がある。
Eに、同じ
try
文でCの左側に宣言されたcatch
句の補足可能例外クラスと代入の互換性がない。
try
文(14.20)では、次のいずれかの場合に例外クラスEをスローできます。
try
ブロックでEをスローすること、リソースの初期化に使用される式(try
-with-resources文内)でEをスローすること、またはリソースのclose()
メソッドの自動呼出し(try
-with-resources文内)でEをスローすることができます。また、Eは、try
文のcatch
句の補足可能例外クラスと代入の互換性がありません。finally
ブロックが存在しないかfinally
ブロックが正常に完了できるかのどちらかです。またはtry
文のcatch
ブロックのいくつかではEをスローできます。finally
ブロックが存在しないかfinally
ブロックを正常に完了できるかのどちらかです。finally
ブロックが存在し、Eをスローできます。
明示的コンストラクタ呼出し文(8.8.7.1)では、次の場合に例外クラスEをスローできます。
コンストラクタ呼出しのパラメータ・リストの式のいくつかでは、Eをスローできます。または
Eは、呼び出されるコンストラクタ(15.12.2.6)の
throws
句の例外クラスであると判断されます。
switch
文(14.11)では、次のいずれかの場合に例外クラスEをスローできます。
セレクタ式では、Eをスローできます。または
一部のswitchルール式、switchルール・ブロック、switchルール
throw
文、またはswitchブロック内のswitchラベル付き文グループでは、Eをスローできます。
Sに直接含まれている式または文でEをスローできる場合は、他の文のSで例外クラスEをスローできます。
明示的コンストラクタ呼出し(8.8.7.1)では、次の場合に例外クラスEをスローできます。
コンストラクタ呼出しの引数リストの式のいくつかでは、Eをスローできます。または
Eは、呼び出されるコンストラクタ(15.12.2.6)の
throws
句の例外クラスであると判断されます。
第12章: 実行
12.5 新しいクラス・インスタンスの作成
クラス・インスタンス作成式(15.9)の評価によってクラスがインスタンス化されると、新しいクラス・インスタンスが明示的に作成されます。
次の状況では、新しいクラス・インスタンスが暗黙的に作成される場合があります。
文字列リテラル(3.10.5)またはテキスト・ブロック(3.10.6)を含むクラスまたはインタフェースをロードすると、文字列リテラルまたはテキスト・ブロックで表される文字列を示す新しい
String
オブジェクトが作成される場合があります。(同じUnicodeコード・ポイントのシーケンスを文字列リテラルまたはテキスト・ブロックの文字列として示すString
のインスタンスが以前にインターン化されている場合は、このオブジェクト作成は発生しません。)ボックス化変換を引き起こす操作の実行(5.1.7)。ボックス化変換によって、プリミティブ型のいずれかに関連付けられたラッパー・クラス(
Boolean
、Byte
、Short
、Character
、Integer
、Long
、Float
、Double
)の新しいオブジェクトを作成できます。定数式(15.29)の一部ではない文字列連結演算子
+
(15.18.1)を実行すると、結果を表す新しいString
オブジェクトが常に作成されます。文字列連結演算子では、プリミティブ型の値に対して一時ラッパー・オブジェクトを作成することもできます。メソッド参照式(15.13.3)またはラムダ式(15.27.4)の評価では、関数インタフェース型(9.8)を実装するクラスの新しいインスタンスの作成が必要になる場合があります。
このようなそれぞれの状況は、クラス・インスタンスの作成プロセスの一部として、指定された引数(ない場合もある)で呼び出される特定のコンストラクタ(8.8)を識別します。
新しいクラス・インスタンスが作成されるときは、常に、クラスで宣言されたすべてのインスタンス変数とクラスの各スーパークラスで宣言されたすべてのインスタンス変数(隠すことができるすべてのインスタンス変数を含む)のための空き領域を確保して、新しいクラス・インスタンスにメモリー領域が割り当てられます(8.3)。
オブジェクトのメモリーを割り当てるための空き領域が不十分である場合、OutOfMemoryError
によってクラス・インスタンスの作成が突然完了します。それ以外の場合は、スーパークラスで宣言された変数を含め、新しいオブジェクトのすべてのインスタンス変数がデフォルト値(4.12.5)に初期化されます。
新しく作成されたオブジェクトへの参照が結果として返される直前に、指定されたコンストラクタが処理され、次の手順に従って新しいオブジェクトが初期化されます。
コンストラクタの引数を、このコンストラクタ呼出しのために新しく作成されたパラメータ変数に割り当てます。
このコンストラクタが(
this
を使用して)同一クラス内の別のコンストラクタの明示的なコンストラクタ呼出し(8.8.7.1)で始まる場合、その引数を評価し、この同じ5つのステップを使用して、コンストラクタの再帰的な呼出しを処理します。コンストラクタの呼出しが突然完了する場合、この手順は同じ理由で突然完了します。そうでない場合は、ステップ5から続行します。このコンストラクタは、(
this
を使用する)同一クラス内の別のコンストラクタの明示的なコンストラクタ呼出しからは始まりません。このコンストラクタがObject
以外のクラス用の場合、このコンストラクタは、(super
を使用する)スーパークラス・コンストラクタの明示的または暗黙的な呼出しから始まります。これら5つのステップを使用して、スーパークラス・コンストラクタを再帰的に呼び出す引数とプロセスを評価します。コンストラクタの呼出しが突然完了する場合、この手順は同じ理由で突然完了します。それ以外の場合は、ステップ4に進みます。このクラスのインスタンス・イニシャライザおよびインスタンス変数イニシャライザを実行し、インスタンス変数イニシャライザの値を対応するインスタンス変数に左から右へ順に割り当て、クラスのソース・コードにテキストで表示されるようにします。これらのイニシャライザのいずれかを実行して例外が発生した場合、それ以上のイニシャライザは処理されず、この手順が同じ例外で突然完了します。それ以外の場合は、ステップ5に進みます。
このコンストラクタの残りの本体を実行します。例外が突然完了する場合、この手順は同じ理由で突然完了します。それ以外の場合、この手順は正常に完了します。
コンストラクタの引数を、このコンストラクタ呼出しのために新しく作成されたパラメータ変数に割り当てます。
このコンストラクタに明示的コンストラクタ呼出し(8.8.7.1)が含まれていない場合は、ステップ5から続行します。
コンストラクタ本体のプロローグのBlockStatementsを実行します(存在する場合)。文の実行が突然完了した場合は、同じ理由でコンストラクタの実行が突然完了します。そうでない場合は、次のステップに進みます。
明示的コンストラクタ呼出しは、同じクラス内の別のコンストラクタの呼出し(
this
を使用)、またはスーパークラス・コンストラクタの呼出し(super
を使用)のどちらかです。コンストラクタ呼出しの引数を評価し、同じ7つのステップを使用してコンストラクタ呼出しを再帰的に処理します。コンストラクタ呼出しが突然完了した場合は、同じ理由でこの手順が突然完了します。そうでない場合は、呼出しが同じクラス内の別のコンストラクタの呼出しであればステップ7から続行し、スーパークラス・コンストラクタの呼出しであればステップ6から続行します。このコンストラクタが
Object
以外のクラス用である場合、このコンストラクタには、引数なしで、スーパークラス・コンストラクタの暗黙的な呼出しが含まれています。この場合は、これらの同じ7つのステップを使用して暗黙的コンストラクタ呼出しを再帰的に処理します。コンストラクタ呼出しが突然完了した場合は、同じ理由この手順が突然完了します。そうでない場合は、次のステップに進みます。このクラスのインスタンス・イニシャライザおよびインスタンス変数イニシャライザを実行し、インスタンス変数イニシャライザの値を対応するインスタンス変数に左から右へ順に割り当て、クラスのソース・コードにテキストで表示されるようにします。これらのイニシャライザのいずれかの実行で例外が発生した場合は、それ以降のイニシャライザは処理されず、同じ例外でこの手順が突然完了します。そうでない場合は、次のステップに進みます。
このコンストラクタのエピローグのBlockStatementsを実行します(存在する場合)。文の実行が突然完了した場合は、同じ理由でこの手順が突然完了します。それ以外の場合、この手順は正常に完了します。
C++とは異なり、Javaプログラミング言語では、新しいクラス・インスタンスの作成中にメソッド・ディスパッチに対するルール変更は指定されません。オブジェクトの初期化中、サブクラスでオーバーライドされるメソッドが呼び出された場合、新しいオブジェクトが完全に初期化される前でも、これらのオーバーライド・メソッドが使用されます。クラスは、コンストラクタ本体のプロローグでフィールドに代入することで、初期化されていない状態の不要な露出を回避できます。
例12.5-1.インスタンス作成の評価
class Point {
int x, y;
Point() { x = 1; y = 1; }
}
class ColoredPoint extends Point {
int color = 0xFF00FF;
}
class Test {
public static void main(String[] args) {
ColoredPoint cp = new ColoredPoint();
System.out.println(cp.color);
}
}
ここでは、ColoredPoint
の新しいインスタンスが作成されます。最初に、フィールドx
、y
およびcolor
を保持するために、新しいColoredPoint
に領域が割り当てられます。これらのフィールドはすべてデフォルト値(この場合は、各フィールドが0
)に初期化されます。次に、引数のないColoredPoint
コンストラクタが最初に呼び出されます。ColoredPoint
はコンストラクタを宣言しないため、次の形式のデフォルトのコンストラクタが暗黙的に宣言されます。
ColoredPoint() { super(); }
このコンストラクタは、引数なしでPoint
コンストラクタを呼び出します。Point
コンストラクタはコンストラクタの呼出しで始まらないため、Javaコンパイラでは、引数なしのスーパークラス・コンストラクタの暗黙的な呼出しが次のような記述で指定されます。
Point() { super(); x = 1; y = 1; }
したがって、引数をとらないObject
のコンストラクタが呼び出されます。
クラスObject
にはスーパークラスがないため、再帰がここで終了します。次に、Object
のインスタンス・イニシャライザおよびインスタンス変数イニシャライザが呼び出されます。次に、引数をとらないObject
のコンストラクタの本体が実行されます。このようなコンストラクタはObject
で宣言されないため、Javaコンパイラは、このような特殊なケースでは次のようなデフォルトのコンストラクタを指定します。
Object() { }
このコンストラクタの実行は効果がないまま返されます。
次に、クラスPoint
のインスタンス変数のすべてのイニシャライザが実行されます。この場合、x
およびy
の宣言では初期化式を指定しないため、この例のステップでは何のアクションも必要ありません。その後、Point
コンストラクタの本体が実行され、x
が1
に、y
が1
に設定されます。
次に、クラスColoredPoint
のインスタンス変数のイニシャライザが実行されます。このステップでは、値0xFF00FF
をcolor
に割り当てます。最後に、ColoredPoint
コンストラクタの本体の残りの部分エピローグ(super
の呼出し後の部分)が実行されます。本体の残りの部分エピローグには文がないため、それ以降のアクションは必要なく、初期化が完了します。
例12.5-2.インスタンス作成時の動的ディスパッチ
class Super {
Super() { printThree(); }
void printThree() { System.out.println("three"); }
}
class Test extends Super {
int three = (int)Math.PI; // That is, 3
void printThree() { System.out.println(three); }
public static void main(String[] args) {
Test t = new Test();
t.printThree();
}
}
このプログラムでは、次の出力が生成されます。
0
3
これは、クラスSuper
のコンストラクタでのprintThree
の呼出しによって、クラスSuper
のprintThree
の定義が呼び出されるのではなく、クラスTest
のprintThree
のオーバーライド定義が呼び出されることを示しています。したがって、このメソッドは、Test
のフィールド・イニシャライザが実行される前に実行されます。このため、最初の値の出力は0
、つまりTest
のフィールドthree
が初期化されるデフォルト値になります。その後、メソッドmain
でprintThree
を呼び出すと、printThree
の同じ定義が呼び出されますが、その時点でインスタンス変数three
のイニシャライザが実行されているため、値3
が出力されます。
例12.5-3.プロローグでのフィールドの初期化
class Super {
Super() { printThree(); }
void printThree() { System.out.println("three"); }
}
class Test extends Super {
int three;
public Test() {
three = (int)Math.PI; // That is, 3
super();
}
void printThree() { System.out.println(three); }
public static void main(String[] args) {
Test t = new Test();
t.printThree();
}
}
この例12.5-2の代替は、次の出力を生成します:
3
3
フィールドthree
は、Test
クラスのコンストラクタのプロローグで初期化されるため、その代入は、ステップ4のSuper
クラスのコンストラクタ本体を評価する前に、オブジェクト初期化プロシージャのステップ3で行われます。この方法でthree
を初期化すると、それはデフォルト値0
によって監視できなくなります。
第14章: ブロックと文およびパターン
14.17 return
文
return
文は、メソッド(8.4、15.12)またはコンストラクタ(8.8、15.9)の起動元に制御を戻します。
- ReturnStatement:
-
return
[Expression];
return
文には、次の2つの種類があります:
値のない
return
文。値が式の
return
文。
return
文は、最も内側で包含しているコンストラクタ、メソッドまたはラムダ式の起動元に制御を転送しようとします。この包含している宣言または式は、returnターゲットと呼ばれます。値が式のreturn
文の場合は、その式の値が呼出しの値になります。
return
文にreturnターゲットがない場合は、コンパイル時にエラーが発生します。
returnターゲットに(i) return
文を囲むインスタンスまたは静的イニシャライザ、または(ii) return
文を囲むswitch
式のいずれかが含まれている場合は、コンパイル時にエラーが発生します。
値が指定されていないreturn
文のreturnターゲットがメソッドで、そのメソッドがvoid
として宣言されていない場合は、コンパイル時にエラーが発生します。
return
文のreturnターゲットがコンストラクタのときに、このコンストラクタのプロローグ(8.8.7)にreturn
文があると、コンパイル時にエラーが発生します。
値が式のreturn
文のreturnターゲットがコンストラクタまたはvoid
と宣言されたメソッドの場合は、コンパイル時にエラーが発生します。
値が式のreturn
文のreturnターゲットが、戻り値の型がTと宣言されたメソッドで、式の型にTとの割当て可能性の互換性がない場合(5.2)は、コンパイル時にエラーが発生します。
値を指定しないreturn
文の実行は、値のないreturnが存在するために常に突然完了します。
値が式のreturn
文を実行すると、その式が最初に評価されます。式の評価がなんらかの理由で突然完了する場合、その理由によってreturn
文は突然完了します。式の評価が正常に完了し、値Vが生成されると、値がVのreturnが存在するためにreturn
文は突然完了します。
そのことから、
return
文が常に突然完了することがわかります。
前述の説明では、単に「制御を転送する」ではなく「制御を転送しようとする」と表現しています。これは、
try
ブロックまたはcatch
句にreturn
文が含まれているメソッドまたはコンストラクタ内にtry
文(14.20)がある場合、該当するtry
文のfinally
句は、起動元のメソッドまたはコンストラクタに制御が転送される前に、最も内側から最も外側の順に実行されます。finally
句が突然完了すると、return
文によって開始された制御の転送が中断されることがあります。
14.22 到達不可能な文
到達不可能であるために文を実行できない場合は、コンパイル時にエラーが発生します。
この項では、"到達可能"という言葉を正確に説明します。それは、文を含むコンストラクタ、メソッド、インスタンス・イニシャライザまたは静的イニシャライザの先頭からその文自体までの実行パスが存在する必要があるという意味です。分析では文の構造が考慮されます。条件式に定数値
true
があるwhile
、do
およびfor
文の特別な処理を除き、式の値は、フロー分析では考慮されません。
たとえば、Javaコンパイラでは、次のコードが受け入れられます。
{ int n = 5; while (n > 7) k = 2; }
コンパイル時に
n
の値がわかっている場合でも、原則として、コンパイル時に、k
への代入を実行できないとわかることがあります。
この項で示すルールでは、次の2つの技術用語の意味を明らかにします。
文に到達可能かどうか
文が正常完了可能かどうか
ルールでは、文は、それに到達可能である場合のみ、正常完了可能です。
さらに2つの技術用語を使用します。
到達可能な
break
文では、ブレーク・ターゲット内で、try
ブロックにbreak
文が含まれているtry
文がない場合、またはtry
ブロックにbreak
文が含まれているtry
文がありそれらのtry
文のすべてのfinally
句が正常完了可能である場合、文が終了されます。この定義は、[14.15]の"制御の転移を試みる"という論理に基づいています。
continue
文では、do
文内で、try
ブロックにcontinue
文が含まれているtry
文がない場合、またはtry
ブロックにcontinue
文が含まれているtry
文がありそれらのtry
文のすべてのfinally
句が正常完了可能である場合、do
文に進みます。
この規則は次のとおりです。
コンストラクタ、メソッド、インスタンス・イニシャライザ、静的イニシャライザ、ラムダ式または
switch
式の本体であるブロックは到達可能です。switchブロックではない空のブロックは、それに到達可能な場合は、正常完了可能です。
switchブロックまたは明示的コンストラクタ呼出しを含むコンストラクタ本体ではない、空でないブロックは、その中の最後の文が正常完了可能な場合は、正常完了可能です。
switchブロックまたは明示的コンストラクタ呼出しを含むコンストラクタ本体ではない、空でないブロック内の最初の文は、そのブロックに到達可能な場合は、到達可能です。
switchブロックまたは明示的コンストラクタ呼出しを含むコンストラクタ本体ではない、空でないブロック内の他のすべての文Sは、Sより前の文が正常完了可能な場合は、到達可能です。
明示的コンストラクタ呼出しを含むコンストラクタの本体ではない、空でないブロックは、その中の最後の文が正常完了可能な場合は、正常完了可能です。
明示的コンストラクタ呼出しを含むコンストラクタ本体の、空でないプロローグ内の最初の文は、そのブロックに到達可能な場合は、到達可能です。
明示的コンストラクタ呼出しを含むコンストラクタ本体のプロローグ内の他のすべての文Sは、Sより前の文が正常完了可能な場合は、到達可能です。
明示的コンストラクタ呼出しを含むコンストラクタの、空でないエピローグ内の最初の文は、そのブロックに到達可能な場合は、到達可能です。
明示的コンストラクタ呼出しを含むコンストラクタの、空でないエピローグ内の最初の文は、そのコンストラクタのプロローグの最後の文が正常完了可能な場合は、到達可能です。
明示的コンストラクタ呼出しを含むコンストラクタのエピローグ内の他のすべての文Sは、Sより前の文が正常完了可能な場合は、到達可能です。
編集部: 14.22項の残りの部分に変更はありません。
第15章: 式
15.8 1次式
15.8.3 this
キーワードthis
は、次のコンテキストで式として使用できます。
クラスのインスタンス・メソッドの本体において(8.4.3.2)
クラスのコンストラクタの本体内(8.8.7)
クラスのインスタンス・イニシャライザ内(8.6)
クラスのインスタンス変数のイニシャライザ内(8.3.2)
インタフェースのインスタンス・メソッドの本体において。つまり、デフォルト・メソッド、または
static
でないprivate
インタフェース・メソッド(9.4)
式として使用したときは、キーワードthis
では、インスタンス・メソッドを呼び出したオブジェクト(15.12)または構成されるオブジェクトへの参照である値が示されます。ラムダ本体(15.27.2)においてthis
で示される値は、その周囲のコンテキストにおいてthis
で示される値と同じです。
キーワード
this
は、明示的コンストラクタ呼出し文呼出し(8.8.7.1)でも使用され、メソッドまたはコンストラクタのレシーバ・パラメータを示すためにも使用されます(8.4)。
静的コンテキストにthis
式が出現すると、コンパイル時にエラーが発生します(8.1.3)。
Cは、this
式の、最も内側の、包含するクラスまたはインタフェースの宣言であるとします。
this
式がCの早期構築コンテキスト(8.8.7)に出現した場合、コンパイル時にエラーが発生します。ただし、単純な代入式(15.26)の左側のオペランドとして指定される、フィールド・アクセス式(15.11)の修飾子として出現し、単純な代入式がCの早期構築コンテキストに含まれるラムダ式または内部クラス宣言に含まれていない場合を除きます。
Cが汎用であり、型パラメータがF1,...,Fnである場合、this
の型はC<
F1,...,Fn>
です。そうでない場合、this
の型はCです。
実行時に、参照される実際のオブジェクトのクラスは、C、またはCのサブクラス(8.1.5)である場合があります。
例15.8.3-1.this
式
class IntVector {
int[] v;
boolean equals(IntVector other) {
if (this == other)
return true;
if (v.length != other.v.length)
return false;
for (int i = 0; i < v.length; i++) {
if (v[i] != other.v[i]) return false;
}
return true;
}
}
ここでは、クラスIntVector
は2つのベクターを比較するメソッドequals
を実装します。一方のベクターが、equals
メソッドが呼び出されたものと同じベクター・オブジェクトの場合、長さと値の比較チェックをスキップできます。equals
メソッドでは、もう一方のオブジェクトへの参照をthis
と比較してこのチェックを実装します。
15.8.4 修飾されたthis
字句的な包含インスタンス(8.1.3)は、キーワードthis
を明示的に修飾することによって参照できます。
nは整数であり、TypeNameで、修飾されたthis
式を宣言で直接包含するクラスまたはインタフェースの、n番目の、字句として包含するクラスまたはインタフェースの宣言が示されているとします。
修飾されたthis
式のTypeName.this
の値は、this
の、n番目の、字句として包含するインスタンスです。
TypeNameで汎用クラスが示されおり、型パラメータがF1,...,Fnである場合、修飾されたthis
式の型は、TypeName<
F1,...,Fn>
です。そうでない場合、修飾されたthis
式の型は、TypeNameです。
修飾されたthis
式が静的コンテキストに出現すると、コンパイル時にエラーが発生します(8.1.3)。
修飾されたthis
式がTypeNameで指定されたクラスの早期構築コンテキスト(8.8.7)に出現した場合、コンパイル時にエラーが発生します。ただし、単純な代入式(15.26)の左側のオペランドとして指定される、フィールド・アクセス式(15.11)の修飾子として出現し、単純な代入式がTypeNameで指定されたクラスの早期構築コンテキストに含まれるラムダ式または内部クラス宣言に含まれていない場合を除きます。
修飾されたthis
式を宣言で直接包含するクラスまたはインタフェースが、TypeNameの内部クラスまたはTypeName自体でない場合は、コンパイル時にエラーが発生します。
15.9 クラス・インスタンス作成式
15.9.2 包含インスタンスの特定
Cはインスタンス化されるクラス、iは作成されるインスタンスであるとします。Cが内部クラスである場合、iには、直接包含するインスタンス(8.1.3)がある可能性があり、次のように特定されます。
Cが無名クラスである場合は、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、iには、直接包含するインスタンスはありません。Cの直接スーパークラスが、静的コンテキストに出現する内部ローカル・クラスLである場合、Sを、Lの宣言を含む最も近い
static
メソッド宣言、static
フィールド宣言または静的イニシャライザにします。クラス・インスタンス作成式を含む最も近いstatic
メソッド宣言、static
フィールド宣言または静的イニシャライザがSでない場合は、コンパイル時にエラーが発生します。編集部: 前述の新しいアサーションは、JEP 395で導入された拡張機能のバグ修正です。
そうでない場合、iを直接包含するインスタンスは
this
です。
Cが内部ローカル・クラスの場合は、次のようになります。
Cが静的コンテキストに出現する場合、iには、直接包含するインスタンスはありません。Sを、Cの宣言を含む最も近い
static
メソッド宣言、static
フィールド宣言または静的イニシャライザにします。クラス・インスタンス作成式を含む最も近いstatic
メソッド宣言、static
フィールド宣言または静的イニシャライザがSでない場合は、コンパイル時にエラーが発生します。編集部: 前述の新しいアサーションは、JEP 395で導入された拡張機能のバグ修正です。
そうでない場合、クラス・インスタンス作成式が静的コンテキストに出現すると、コンパイル時にエラーが発生します。
そうでない場合、Oは直前と直後のCのクラスまたはインタフェース宣言、Uは直前と直後のクラス・インスタンス作成式のクラスまたはインタフェース宣言とします。
UがOの内部クラスまたはO自体ではない場合、コンパイル時にエラーが発生します。
nは整数であり、Oが、Uの、n番目の字句的に包含するクラスまたはインタフェースの宣言であるとします。
iを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。
Cが内部メンバー・クラスである場合は、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
そうでない場合、Cが、クラス・インスタンス作成式が宣言で字句として包含されているクラスのメンバーでなければ、コンパイル時にエラーが発生します。
そうでない場合、OをCがメンバーである最も内側の包含クラス宣言にし、Uを直前と直後のクラス・インスタンス作成式のクラスまたはインタフェース宣言にします。
UがOの内部クラスまたはO自体ではない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式がOの早期構築コンテキスト(8.8.7)に示されると、コンパイル時にエラーが発生します。
nは整数であり、Oが、Uの、n番目の字句的に包含するクラスまたはインタフェースの宣言であるとします。
iを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。
クラス・インスタンス作成式が修飾されている場合、iを直接包含するインスタンスは、Primary式またはExpressionNameの値であるオブジェクトです。
Cが無名クラスで、かつその直接スーパークラスSが内部クラスである場合、iは、Sに関して直接包含するインスタンスを持つ可能性があり、次のように判別されます。
Sが内部ローカル・クラスの場合は、次のようになります。
Sが静的コンテキストに出現する場合、iには、Sに関して直接包含するインスタンスはありません。
そうでない場合、クラス・インスタンス作成式が静的コンテキストに出現すると、コンパイル時にエラーが発生します。
そうでない場合、Oを直前と直後のSのクラスまたはインタフェース宣言にし、Uを直前と直後のクラス・インスタンス宣言式のクラスまたはインタフェース宣言にします。
UがOの内部クラスまたはO自体ではない場合、コンパイル時にエラーが発生します。
nは整数であり、Oが、Uの、n番目の字句的に包含するクラスまたはインタフェースの宣言であるとします。
Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。
Sが内部メンバー・クラスである場合は、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
そうでない場合、Sはその宣言がクラス・インスタンス作成式を囲んでいるクラスのメンバーではなく、コンパイル時にエラーが発生します。
そうでない場合、OをSがメンバーである、最も内側の包含クラス宣言にし、Uを直前と直後のクラス・インスタンス作成式のクラスまたはインタフェース宣言にします。
UがOの内部クラスまたはO自体ではない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式がOの早期構築コンテキストに示されると、コンパイル時にエラーが発生します。
nは整数であり、Oが、Uの、n番目の字句的に包含するクラスまたはインタフェースの宣言であるとします。
Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。そうでない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式が修飾されている場合、Sに関してiを直接包含するインスタンスは、Primary式またはExpressionNameの値であるオブジェクトです。
15.11 フィールド・アクセス式
15.11.2 super
を使用したスーパークラス・メンバーへのアクセス
super.
Identifierの形式は、現在のオブジェクトのIdentifierというフィールドを参照していますが、その現在のオブジェクトは、現在のクラスのスーパークラスのインスタンスとして表示されます。
T.super.
Identifierという形式は、Tに対応する語彙的な包含インスタンスのIdentifierというフィールドを参照しますが、そのインスタンスはTのスーパークラスのインスタンスとして表示されます。
キーワードsuper
を使用する形式は、キーワードthis
を式(15.8.3)として使用できるクラス宣言内の場所で使用できます。
キーワードsuper
を使用するフィールド・アクセス式が静的コンテキスト(8.1.3)または現在のクラスの早期構築コンテキスト(8.8.7)に出現出現する場合は、コンパイル時にエラーが発生します。
super.
Identifierという形式のフィールド・アクセス式の場合:
- フィールド・アクセス式の、直接包含するクラスまたはインタフェースの宣言がクラス
Object
またはインタフェースである場合は、コンパイル時にエラーが発生します。
T.super.
Identifierという形式のフィールド・アクセス式の場合:
Tがクラス
Object
またはインタフェースの場合、コンパイル時にエラーが発生します。Uは、フィールド・アクセス式の、直接包含するクラスまたはインタフェースの宣言であるとします。UがTの内部クラスまたはT自体ではない場合、コンパイル時にエラーが発生します。
フィールド・アクセス式super.
fがクラスCに出現し、Cの直接スーパークラスがSであるとします。S内のfにクラスCからアクセスできる場合(6.6)、super.
fはクラスS本体内の式this.
fであったものとして扱われます。そうでない場合、コンパイル時にエラーが発生します。
このため、
super.
fはクラスS内でアクセス可能なフィールドfがクラスC内のフィールドfの宣言で非表示にされていても、そのフィールドにアクセスできます。
フィールド・アクセス式T.super.
fがクラスC内に出現し、Tが示すクラスの直接スーパークラスが完全修飾名Sのクラスであるとします。S内のfがCからアクセス可能な場合、T.super.
fはクラスSの本体内のthis.
f式であったものとして扱われます。そうでない場合、コンパイル時にエラーが発生します。
このため、T
.super.
fは、クラスS内でアクセス可能なフィールドfがクラスT内のフィールドfの宣言によって非表示にされていても、そのフィールドにアクセスできます。
例15.11.2-1.super
式
interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
int x = 3;
void test() {
System.out.println("x=\t\t" + x);
System.out.println("super.x=\t\t" + super.x);
System.out.println("((T2)this).x=\t" + ((T2)this).x);
System.out.println("((T1)this).x=\t" + ((T1)this).x);
System.out.println("((I)this).x=\t" + ((I)this).x);
}
}
class Test {
public static void main(String[] args) {
new T3().test();
}
}
このプログラムでは、次の出力が生成されます。
x= 3
super.x= 2
((T2)this).x= 2
((T1)this).x= 1
((I)this).x= 0
クラスT3
内では、式super.x
はx
にパッケージ・アクセスがあれば、実質的に((T2)this).x
と同じです。super.x
はスーパークラスのprotected
メンバーへのアクセスが困難なため、キャストについては指定されていません。
15.12 メソッド呼出し式
15.12.3 コンパイル時のステップ3: 選択したメソッドが適切かどうか
メソッド呼出しについて最も的確なメソッド宣言がある場合、メソッド呼出しのコンパイル時宣言と呼ばれます。
メソッド呼出しの引数が、コンパイル時宣言の呼出しタイプから導出されたそのターゲット型と互換性がない場合は、コンパイル時にエラーが発生します。
コンパイル時宣言が可変引数呼出しによって適用可能である場合、メソッドの呼出しタイプの最後の仮パラメータ型がFn[]
であるとすると、Fnのイレイジャである型が呼出しの時点でアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。
コンパイル時宣言がvoid
である場合、メソッド呼出しは、最上位式(つまり、式の文にあるか、for
文のForInitまたはForUpdate部分にあるExpression)である必要があります。そうでない場合はコンパイル時にエラーが発生します。このようなメソッド呼出しは値を生成しないため、値が不要な状況でのみ使用する必要があります。
さらに、コンパイル時宣言が適切であるかどうかは、次のように、左カッコの前にあるメソッド呼出し式の形式によって異なることがあります。
形式がMethodName、つまり単にIdentifierであり、かつコンパイル時宣言がインスタンス・メソッドである場合は、次のようになります。
形式がTypeName
.
[TypeArguments] Identifierである場合、コンパイル時宣言はstatic
である必要があります。そうでない場合はコンパイル時にエラーが発生します。形式がExpressionName
.
[TypeArguments] IdentifierまたはPrimary.
[TypeArguments] Identifierである場合、コンパイル時宣言は、インタフェースで宣言されたstatic
メソッドでないことが必要です。そうである場合はコンパイル時にエラーが発生します。形式が
super
.
[TypeArguments] Identifierである場合は、次のようになります。コンパイル時宣言が
abstract
である場合、コンパイル時にエラーが発生します。メソッド呼出しが静的コンテキストまたは現在のクラスの早期構築コンテキストに示されると、コンパイル時にエラーが発生します。
形式がTypeName
.
super
.
[TypeArguments] Identifierである場合は、次のようになります。コンパイル時宣言が
abstract
である場合、コンパイル時にエラーが発生します。メソッド呼出しが静的コンテキストまたは現在のクラスの早期構築コンテキストに示されると、コンパイル時にエラーが発生します。
TypeNameでクラスCが示されている場合に、メソッド呼出しを直接包含するクラス宣言またはインタフェース宣言がCでもCの内部クラスでもなければ、コンパイル時にエラーが発生します。
TypeNameがインタフェースを示している場合、Eをメソッド呼出しを直接包含するクラスまたはインタフェース宣言とします。Eの直接スーパークラスまたは直接スーパーインタフェースからのコンパイル時宣言をオーバーライド(9.4.1)する、コンパイル時宣言とは異なるメソッドが存在する場合、コンパイル時にエラーが発生します。
親インタフェースの親で宣言されたメソッドをスーパーインタフェースがオーバーライドする場合、このルールでは、親の親を直接スーパーインタフェースのそのリストに単に追加することによって、子インタフェースがオーバーライドをスキップすることが回避されます。親の親の機能にアクセスするための適切な方法は、直接スーパーインタフェースを介して、そのインタフェースが目的の動作を公開することを選択する場合にのみ、アクセスすることです。(また、プログラマは、
super
メソッド呼出しで目的の動作を公開する追加のスーパーインタフェースを自由に定義できます。)
compile-timeパラメータ型とcompile-time結果は次のように決定されます。
メソッド呼出しのコンパイル時宣言がシグネチャ多相メソッドではない場合は次のようになります。
コンパイル時のパラメータ型は、コンパイル時宣言の仮パラメータの型です。
コンパイル時の結果は、コンパイル時宣言の呼出し型の結果です(15.12.2.6)。
メソッド呼出しのコンパイル時宣言がシグネチャ多相メソッドの場合、次のようになります。
コンパイル時のパラメータ型は、実引数式の型です。nullリテラル
null
(3.10.8)の引数式は、型Void
を持つものとして扱われます。コンパイル時の結果は次のように決定されます。
メソッドは、次がすべてtrueの場合にシグネチャ多相になります。
java.lang.invoke.MethodHandle
クラスまたはjava.lang.invoke.VarHandle
クラスで宣言されている。宣言型が
Object[]
の単一の可変個引数パラメータ(8.4.1)がある。native
である。
次のコンパイル時の情報は、実行時に使用するメソッド呼出しに関連するものです。
メソッドの名前。
メソッド呼出しの修飾クラスまたはインタフェース(13.1)。
順に並べたパラメータの数、およびコンパイル時のパラメータ型。
コンパイル時の結果。
次のように計算された呼出しモード。
コンパイル時宣言に
static
修飾子がある場合、呼出しモードはstatic
です。そうでない場合、左側のカッコの前のメソッド呼出しの部分が
super
.
という形式になりますIdentifier、またはTypeName.
super
.
というの形式のものIdentifier。呼出しモードはsuper
です。そうでない場合、メソッド呼出しの修飾クラスまたはインタフェースが実際はインタフェースであれば、呼出しモードは
interface
です。そうでない場合、呼出しモードは
virtual
です。
コンパイル時宣言の呼出し型の結果がvoid
の場合、メソッド呼出し式の型は、コンパイル時宣言の呼出し型の戻り型へのキャプチャ変換を適用して取得します(5.1.10)。
15.13 メソッド参照式
呼出しを実際に実行することなく、メソッドの呼出しを参照するために、メソッド参照式が使用されます。特定の形式のメソッド参照式でも、メソッド呼出しと同様に、クラス・インスタンスの作成(15.9)または配列の作成(15.10)を処理できます。
- MethodReference:
-
ExpressionName
::
[TypeArguments] Identifier -
Primary
::
[TypeArguments] Identifier -
ReferenceType
::
[TypeArguments] Identifier -
super
::
[TypeArguments] Identifier -
TypeName
.
super
::
[TypeArguments] Identifier -
ClassType
::
[TypeArguments]new
-
ArrayType
::
new
::
の右側にTypeArgumentsが存在する場合、いずれかの型引数がワイルドカード(4.5.1)であると、コンパイル時にエラーが発生します。
メソッド参照式の形式がExpressionName ::
[TypeArguments] IdentifierまたはPrimary ::
[TypeArguments] Identifierの場合、ExpressionNameまたはPrimaryの型が参照型ではないと、コンパイル時にエラーが発生します。
メソッド参照式の形式がsuper
::
[TypeArguments] Identifierである場合、Eは、メソッド参照式を直接包含するクラス宣言またはインタフェース宣言であるとします。EがクラスObject
であるかEがインタフェースである場合は、コンパイル時にエラーが発生します。
メソッド参照式の形式がTypeName .
super
::
[TypeArguments] Identifierの場合、次のようになります。
TypeNameがクラスCを示す場合に、Cが現在のクラスの字句的な包含クラスではない場合、またはCがクラス
Object
である場合は、コンパイル時にエラーが発生します。TypeNameでインタフェースIが示されている場合に、Eは、メソッド参照式を直接包含するクラス宣言またはインタフェース宣言であるとします。IがEの直接スーパーインタフェースではない場合、またはEに他の直接スーパークラスか直接スーパーインタフェースJが存在し、JがIのサブクラスまたはサブインタフェースであるような場合は、コンパイル時にエラーが発生します。
TypeNameが型変数を示す場合、コンパイル時にエラーが発生します。
メソッド参照式の形式がsuper
::
[TypeArguments] IdentifierまたはTypeName .
super
::
[TypeArguments] Identifierの場合は、その式が静的コンテキスト(8.1.3)または現在のクラスの早期構築コンテキスト(8.8.7)に示されると、コンパイル時にエラーが発生します。
メソッド参照式の形式がClassType ::
[TypeArguments] new
の場合、次のようになります。
ClassTypeでは、アクセス可能(6.6)であり、
abstract
ではなく、列挙クラスではないクラスが指定されている必要があります。そうでない場合は、コンパイル時にエラーが発生します。ClassTypeが、パラメータ化された型(4.5)を示す場合に、そのいずれかの型引数がワイルドカードであると、コンパイル時にエラーが発生します。
ClassTypeが、RAW型(4.8)を示す場合に、
::
の後にTypeArgumentsが存在すると、コンパイル時にエラーが発生します。
メソッド参照式の形式がArrayType ::
new
の場合、ArrayTypeは、具象化可能(4.7)である型を示している必要があります。そうでない場合、コンパイル時にエラーが発生します。
インスタンス・メソッドのターゲット参照(15.12.4.1)は、ExpressionName、Primaryまたはsuper
を使用してメソッド参照式によって提供するか、後でそのメソッドを呼び出すときに提供することができます。新しい内部クラス・インスタンス(15.9.2)の直接包含インスタンスは、this
の、字句として包含するインスタンス(8.1.3)によって提供されます。
型の複数のメンバー・メソッドに同じ名前が付いている場合、またはクラスに複数のコンストラクタがある場合は、15.13.1で示されているように、メソッド参照式によってターゲット指定された関数インタフェース型に基づいて適切なメソッドまたはコンストラクタが選択されます。
メソッドまたはコンストラクタが汎用である場合は、適切な型引数を推測するか明示的に指定することができます。同様に、メソッド参照式で示される汎用型の型引数は、明示的に指定するか推測することができます。
メソッド参照式は、必ず多重式です(15.2)。
プログラムで代入コンテキスト(5.2)、呼出しコンテキスト(5.3)またはキャスト・コンテキスト(5.5)以外の場所でメソッド参照式が出現する場合は、コンパイル時にエラーが発生します。
メソッド参照式を評価すると、関数インタフェース型のインスタンスが生成されます(9.8)。これにより、対応するメソッドの実行は発生しません。そうではなく、後で関数インタフェースの適切なメソッドが呼び出されたときにその実行が発生する場合があります。
いくつかのメソッド参照式を次に示します。まずはターゲット参照なし、次にターゲット参照ありです。
String::length // instance method System::currentTimeMillis // static method List<String>::size // explicit type arguments for generic type List::size // inferred type arguments for generic type int[]::clone T::tvarMember System.out::println "abc"::length foo[x]::bar (test ? list.replaceAll(String::trim) : list) :: iterator super::toString
さらにいくつかのメソッド参照式を次に示します。
String::valueOf // overload resolution needed Arrays::sort // type arguments inferred from context Arrays::<String>sort // explicit type arguments
オブジェクトまたは配列が遅れて作成されるメソッド参照式を次に示します。
ArrayList<String>::new // constructor for parameterized type ArrayList::new // inferred type arguments // for generic class Foo::<Integer>new // explicit type arguments // for generic constructor Bar<String>::<Integer>new // generic class, generic constructor Outer.Inner::new // inner class constructor int[]::new // array creation
照合する特定の署名(
Arrays::sort(int[])
など)を指定することはできません。かわりに、関数インタフェースでは、オーバーロード解決アルゴリズム(15.12.2)への入力として使用される引数タイプが用意されています。これで大部分のユースケースに対応できます。まれですが、より細かい制御が必要になった場合は、ラムダ式を使用できます。
デリミタ(
List<String>::size
)の前にクラス名で型引数構文を使用すると、解析中に、型引数のカッコとしての<
とより小演算子としての<
の区別に関する問題が発生します。理論的には、これはキャスト式で型引数を許可するより悪いことではありません。ただし、その違いは、キャストのケースは(
トークンが検出されたときのみ発生するという点です。メソッド参照式を追加すると、すべての式の先頭が、パラメータ化された型になる可能性があります。
15.13.1 メソッド参照のコンパイル時宣言
メソッド参照式のコンパイル時宣言は、式の参照先のメソッドです。特殊なケースでは、コンパイル時宣言は実際に存在しませんが、クラス・インスタンス作成または配列作成を表す概念上のメソッドが存在します。コンパイル時宣言の選択は、メソッド呼出しのコンパイル時宣言が呼出しの引数に依存するように、式がターゲットとする関数型に依存します(15.12.3)。
コンパイル時宣言の検索では、15.12.1および15.12.2のメソッド呼出しのプロセスを次のようにミラー化します。
最初に、検索する型を決定します。
メソッド参照式の形式がExpressionName
::
[TypeArguments] IdentifierまたはPrimary::
[TypeArguments] Identifierの場合、検索対象の型は::
トークンの前の式の型です。メソッド参照式の形式がReferenceType
::
[TypeArguments] Identifierの場合、検索対象の型は、ReferenceTypeに適用されるキャプチャ変換(5.1.10)の結果です。メソッド参照式の形式が
super
::
[TypeArguments] Identifierである場合、検索対象の型は、メソッド参照式の、直接包含するクラスまたはインタフェースの宣言のスーパークラス型です。Tを、メソッド参照式を直接囲んでいるクラスまたはインタフェース宣言にします。Tがクラス
Object
またはインタフェースの場合、コンパイル時にエラーが発生します。メソッド参照式の形式がTypeName
.
super
::
[TypeArguments] Identifierである場合は、TypeNameでクラスが示されていると、検索対象の型は、指定されたクラスのスーパークラス型になります。そうでない場合は、TypeNameで、検索対象のインタフェースが示されます。TypeNameが、メソッド参照式の、字句として包含するクラスまたはインタフェースの宣言でも、直接包含するクラスまたはインタフェースの宣言の直接スーパーインタフェースでもない場合は、コンパイル時にエラーが発生します。
TypeNameがクラスObjectである場合は、コンパイル時にエラーが発生します。
TypeNameがインタフェースであり、メソッド参照式J (JはTypeNameのサブクラスまたはサブインタフェースである)の、直接包含するクラスまたはインタフェースの宣言の、その他の直接スーパークラスまたは直接スーパーインタフェースが存在する場合は、コンパイル時にエラーが発生します。
他の2つの形式(
::
new
の呼出し)については、参照されているメソッドが概念的なものであり、検索対象の型はありません。
次に、ターゲットの関数型をnパラメータで指定すると、適用可能なメソッドのセットが特定されます。
メソッド参照式の形式がReferenceType
::
[TypeArguments] Identifierの場合、適用可能なメソッドは次のとおりです。名前がIdentifier、引数の数がnであり、型引数TypeArgumentsがあり、メソッド参照式と同じクラスに出現する、メソッド呼出しに適用可能な検索対象のメンバー・メソッド(15.12.2.1)、および
名前がIdentifier、引数の数がn-1であり、型引数TypeArgumentsがあり、メソッド参照式と同じクラスに出現する、メソッド呼出しに適用可能な検索対象の型のメンバー・メソッド。
2つの異なる引数の数、nとn-1で、この形式が
static
メソッドとインスタンス・メソッドのどちらを参照している可能性があるかを判断できます。メソッド参照式の形式がClassType
::
[TypeArguments]new
の場合、適用可能なメソッドは、ClassTypeのコンストラクタに対応する概念的なメソッドのセットです。ClassTypeがRAW型でも、RAW型の非
static
メンバー型ではない場合、概念的なメンバー・メソッドの候補は、<>
を使用してクラスへの型引数を除外するクラス・インスタンス作成式について15.9.3で規定されているメソッドです。そうでない場合、概念的なメンバー・メソッドの候補はClassTypeのコンストラクタであり、戻り型ClassTypeのメソッドとして扱われます。これらの候補のうち適用可能なメソッドは、引数の数がnであり、型引数TypeArgumentsがあり、メソッド参照式として同じクラスに出現するメソッド呼出しに適用可能になる概念的なメソッドです。
メソッド参照式の形式がArrayType
::
new
の場合、単一の概念的なメソッドが考慮されます。メソッドには型int
の単一のパラメータがあり、ArrayTypeを返し、throws
句はありません。n = 1の場合はこれが唯一適用可能なメソッドであり、そうでない場合は適用可能なメソッドがありません。他のすべての形式については、適用可能なメソッドは、名前がIdentifier、引数の数がnであり、型引数TypeArgumentsがあり、メソッド参照式と同じクラスに出現するメソッド呼出しに適用できる、検索対象の型のメンバー・メソッドです。
最終的に、適用可能なメソッドがなければ、コンパイル時宣言もありません。
そうでない場合、パラメータ型P1, ..., Pnのターゲットの関数型および適用可能なメソッドのセットに対して、コンパイル時宣言が次のように選択されます。
メソッド参照式の形式がReferenceType
::
[TypeArguments] Identifierの場合、最も的確で適用可能なメソッドの2種類の検索が実行されます。それぞれの検索は15.12.2.2から15.12.2.5に次のように規定されています。それぞれの検索の結果、適用可能なメソッドのセットが生成され、その中で最も的確なメソッドが指定されます。15.12.2.4に規定されているエラーが発生した場合、適用可能なメソッドのセットが空です。15.12.2.5に規定されているエラーが発生した場合、最も的確なメソッドがありません。最初の検索では、メソッド参照は型P1, ..., Pnの引数式で呼び出されたものとして扱われます。型引数があれば、メソッド参照式で指定されます。
2番目の検索では、P1, ..., Pnが空でなく、P1がReferenceTypeのサブタイプであれば、メソッド参照式は型P2, ..., Pnの引数式のあるメソッド呼出し式として扱われます。ReferenceTypeがRAW型であり、P1のスーパータイプであるこの型G
<
...>
のパラメータ化がある場合、検索対象の型はG<
...>
に適用されるキャプチャ変換の結果(5.1.10)になり、そうでない場合、検索対象の型は最初の検索の型と同じものになります。型引数があれば、メソッド参照式で指定されます。最初の検索で、
static
である最も的確なメソッドが生成され、2番目の検索で生成された適用可能なメソッドのセットに、static
でないメソッドが含まれていない場合、コンパイル時宣言は、最初の検索の、最も的確なメソッドになります。そうでない場合、最初の検索で生成された適用可能なメソッドのセットに
static
メソッドが含まれておらず、2番目の検索で非static
の最も的確なメソッドが生成されると、コンパイル時宣言が2番目の検索の最も的確なメソッドになります。そうでない場合、コンパイル時宣言はありません。
メソッド参照式のその他すべての形式については、最も的確で適用可能なメソッドの検索が1回実行されます。検索については、15.12.2.2から15.12.2.5で次のように規定されています。
メソッド参照は、型P1, ..., Pnの引数式の呼出しとして扱われ、型引数があればメソッド参照式で指定されます。
検索結果が15.12.2.2から15.12.2.5で規定されているエラーになった場合、または最も的確で適用可能なメソッドが
static
の場合、コンパイル時宣言はありません。そうでない場合、コンパイル時宣言が最も的確で適用可能なメソッドになります。
メソッド参照式の形式がReferenceType ::
[TypeArguments] Identifierであり、コンパイル時宣言がstatic
でReferenceTypeが単純名または修飾名ではない場合(6.2)、コンパイル時にエラーが発生します。
メソッド参照式の形式がsuper
::
[TypeArguments] IdentifierまたはTypeName .
super
::
[TypeArguments] Identifierで、コンパイル時宣言がabstract
の場合、コンパイル時にエラーが発生します。
メソッド参照式の形式がsuper
::
[TypeArguments] IdentifierまたはTypeName .
super
::
[TypeArguments] Identifierの場合は、メソッド参照式が静的コンテキスト(8.1.3)または現在のクラスの早期構築コンテキスト(8.8.7)に示されると、コンパイル時にエラーが発生します。
メソッド参照式の形式がTypeName .
super
::
[TypeArguments] Identifierであり、TypeNameでクラスCが示されており、メソッド参照式の、直接包含するクラスまたはインタフェースの宣言がC、またはCの内部クラスでない場合は、コンパイル時にエラーが発生します。
メソッド参照式の形式がTypeName .
super
::
[TypeArguments] Identifierであり、TypeNameでインタフェースが示されており、メソッドがコンパイル時宣言とは別に存在し、それによって、メソッド宣言式(8.4.8, 9.4.1)を直接含む宣言があるクラスまたはインタフェースの直接スーパークラスまたは直接スーパーインタフェースからのコンパイル時宣言がオーバーライドされる場合は、コンパイル時にエラーが発生します。
メソッド参照式の形式がClassType ::
[TypeArguments] new
であり、15.9.2の規定のとおり、ClassTypeの包含インスタンスを決定する際にコンパイル時エラーが発生した場合、コンパイル時にエラーが発生します(メソッド参照式は未修飾クラス・インスタンス作成式として扱われます)。
形式ReferenceType
::
[TypeArguments] Identifierのメソッド参照式は、複数の方法で解釈できます。Identifierがインスタンス・メソッドを参照している場合、Identifierがstatic
メソッドを参照する場合より、暗黙的ラムダ式のパラメータが多くなります。それぞれのケースのパラメータ型は異なるため、ReferenceTypeに適用可能な両方のメソッドを指定すると、それぞれが前述の検索アルゴリズムによって個別に認識されます。
あいまいさの例は次のとおりです。
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } static int size(Object arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // Error: instance method size() // or static method size(Object)? } }
適用可能な
static
メソッドより的確な適用可能インスタンス・メソッドを指定しても、このあいまいさは解決できません。
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } static int size(Object arg) { return 0; } int size(C arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // Error: instance method size() // or static method size(Object)? } }
検索はスマートに行われるためにこのようなあいまいさは無視され、(両方の検索から生成された)適用可能なメソッドがすべてインスタンス・メソッドになります。
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 0; } int size(Object arg) { return 0; } int size(C arg) { return 0; } void test() { Fun<C, Integer> f1 = C::size; // OK: reference is to instance method size() } }
便宜上、汎用型の名前をインスタンス・メソッドの参照に使用する場合(レシーバが最初のパラメータになる場合)ターゲット型が型引数の決定に使用されます。これによって、
Pair<String,Integer>::first
のかわりにPair::first
を使用するなどの用途が促進されます。同様に、Pair::new
などのメソッド参照は「ダイヤモンド」インスタンス作成(new Pair<>()
)として扱われます。「ダイヤモンド」は暗黙的のため、この形式はRAW型をインスタンス化しません。事実、RAW型のコンストラクタへの参照を表現する方法はありません。
一部のメソッド参照式では、ターゲットの関数型に関係なく、使用できるコンパイル時宣言は1つのみであり、その呼出し型も1つのみです(15.12.2.6)。このようなメソッド参照式はexactと呼ばれます。exactではないメソッド参照式はinexactと呼ばれます。
次をすべて満たす場合、Identifierで終了するメソッド参照式はexactです。
メソッド参照式の形式がReferenceType
::
[TypeArguments] Identifierの場合、ReferenceTypeがRAW型を示さない。検索対象の型に、メソッド参照式が出現するクラスまたはインタフェースにアクセスできる、Identifierというメンバー・メソッドが1つだけある。
このメソッドが可変個引数でない(8.4.1)。
このメソッドが汎用(8.4.4)の場合、メソッド参照式でTypeArgumentsが指定される。
次をすべて満たす場合、形式ClassType ::
[TypeArguments] new
のメソッド参照式はexactです。
ClassTypeが示す型がRAW型ではないか、RAW型の非
static
メンバー型である。ClassTypeが示す型に、メソッド参照式が出現するクラスまたはインタフェースにアクセスできるコンストラクタが1つだけある。
このコンストラクタが可変個引数ではない。
このコンストラクタが汎用の場合、メソッド参照式でTypeArgumentsが指定される。
形式ArrayType ::
new
のメソッド参照式は常にexactです。
第16章: 明確な割当て
文で宣言されたすべてのローカル変数(14.4.2、14.14.1、14.14.2、14.20.3)と、すべての空のfinal
フィールド(4.12.4、8.3.1.2)は、その値のアクセス時に確実に割り当てられた値を持っている必要があります。
その値へのアクセスは、式内で単純割当て演算子=
(15.26.1)の左側のオペランド以外の場所に出現する変数の単純名(つまり、フィールドの場合はthis
で修飾されたフィールドの単純名)で構成されます。
文xで宣言されたローカル変数または空のfinal
フィールドxのアクセスでは、そのアクセスの前にxが確実に割り当てられている必要があります。そうしていないと、コンパイル時にエラーが発生します。
同様に、すべての空のfinal
変数は最大で1回割り当てる必要があります。割当てが出現したときには、明確に割当てが解除されている必要があります。
このような割当ては、変数の単純名(つまり、フィールドの場合はthis
で修飾されたその単純名)が割当て演算子の左側に出現する場合にのみ出現するように定義されます。
空のfinal
変数へのすべての割当てについて、割当ての前に変数を明確に割当て解除する必要があります。そうしないと、コンパイル時にエラーが発生します。
同様に、クラスCのコンストラクタ内に現れる代替コンストラクタ呼出し(8.8.7.1)ごとに、Cで宣言したCの空白のfinal
インスタンス変数は、代替コンストラクタ呼出しの引数リストの後に確実に割当て解除する必要があります。そうしていないと、コンパイル時にエラーが発生します。
パターン(14.30)で宣言されたローカル変数は、明確な割当てのルールの対象になりません。パターンで宣言されたすべてのローカル変数は、パターン・マッチングのプロセスで初期化されるため、アクセス時には常に値を持っています。
この章の残りの部分では、「前に明確に割り当てる」および「前に明確に割当て解除する」という語句について正確に説明します。
...
16.2 明確な割当ておよび文
16.2.2 ブロック
編集部: コンストラクタの本体は、特にプロローグ・セクションとエピローグ・セクションに分けられるようになった現在では、適切にブロックと見なすことはできません。コンストラクタ本体の処理については、16.9で説明することになりました。
空の
final
メンバー・フィールドVは、Vのスコープ内にあるメソッドの本体であるブロック(14.2)の前と、Vのスコープ内で宣言されたクラスの宣言の前に、確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。文Sで宣言されたローカル変数Vは、
コンストラクタ、メソッド、インスタンス・イニシャライザ、またはSを含む静的イニシャライザの本体であるブロックの前に、確実に割当て解除されます(さらに言えば、確実に割り当てられていません)。CをVのスコープ内で宣言されたクラスとします。その場合、Cの宣言の前にVが確実に割り当てられていれば、Cで宣言された
コンストラクタ、メソッド、インスタンス・イニシャライザ、または静的イニシャライザの本体であるブロックの前に、Vは確実に割り当てられます。Cで宣言された
コンストラクタ、メソッド、インスタンス・イニシャライザまたは静的イニシャライザの本体であるブロックの前に、Vが確実に割当て解除されていると結論付けることができるルールはありません。Vは、Cで宣言されたコンストラクタ、メソッド、インスタンス・イニシャライザまたは静的イニシャライザの本体であるブロックの前に確実には割当て解除されていないと非公式に結論付けることができますが、そのようなルールを明示的に述べる必要はありません。Vは、Vが空のブロックの前に割当て(割当て解除)されている場合、空のブロックの後に割当て(割当て解除)されます。
Vは、Vがブロック内の最後の文の後に割当て(割当て解除)される場合、空でないブロックの後に割当て(割当て解除)されます。
Vは、Vがブロックの前に割当て(割当て解除)される場合、ブロックの最初の文の前に割当て(割当て解除)されます。
Vは、Vがブロック内のSの直前の文の後に割当て(割当て解除)される場合、ブロックの他の文Sの前に割当て(割当て解除)されます。
Vは、次の場合にブロックB内のどこでも確実に割当て解除されます:
VがBの前に確実に割当て解除されている。
VがB内で発生するすべての代入式V
=
e、V+=
e、V-=
e、V*=
e、V/=
e、V%=
e、V<<=
e、V>>=
e、V>>>=
e、V&=
e、V|=
eまたはV^=
eでeの後に確実に割り当てられている。VがBに現れるすべての式
++
V、--
V、V++
またはV--
の前に確実に割り当てられている。
これらの条件は直感に反するもので、説明が必要です。単純な代入V
=
eについて考えてみます。Vがeの後に確実に割り当てられる場合は、次のいずれかになります:
割当てはデッドコードで発生し、Vは無意味に確実に割り当てられている。この場合、代入は実際には行われず、Vが代入式では割り当てられていないと想定できます。または:
Vは、eより前の式ですでに割り当てられていた。この場合、現在の割当てによってコンパイル時にエラーが発生します。
そのため、コンパイル時にエラーが発生しないプログラムによって条件が満たされていると、BのVへの割当ては実行時に実際には行われないと結論付けられます。
16.9 明確な割当て、コンストラクタおよびインスタンス・イニシャライザ
CをVのスコープ内で宣言されたクラスとします。その場合:
VがCの前に明確に割り当てられている場合、VはCのコンストラクタ宣言(8.8.7)または
インスタンス変数イニシャライザ(8.3.2)の前に確実に割り当てられます。Vが、
コンストラクタ宣言またはインスタンス変数イニシャライザの前に確実に割当て解除されると結論付けることができるルールはありません。Cのコンストラクタ宣言またはインスタンス変数イニシャライザの前に、Vが確実に割当て解除されないことを非公式に結論付けることはできますが、そのようなルールを明示的に述べる必要はありません。
Cをクラスとし、VをCで宣言されたCの空のfinal
非static
メンバー・フィールドとします。その場合:
Vは、C内のコンストラクタの宣言の前に確実に割当て解除されます(さらに言えば、確実に割り当てられていません)。
VがCのコンストラクタ内のすべてのスーパークラス・コンストラクタ呼出し(8.8.7.1)の後に確実に割当て解除されている場合、Vは、Cの左端のインスタンス・イニシャライザ(8.6)またはインスタンス変数イニシャライザの前に確実に割当て解除されます(さらに言えば、確実に割り当てられていません)。
Cが少なくとも1つのコンストラクタを宣言していて、Cのすべてのコンストラクタに明示的なコンストラクタ呼出しがあり、それらのコンストラクタのすべてのスーパークラス・コンストラクタ呼出しの後にVが確実に割り当てられている場合、Vは、Cの左端のインスタンス・イニシャライザ(8.6)またはインスタンス変数イニシャライザの前に確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。
VがCのインスタンス・イニシャライザまたはインスタンス変数イニシャライザの後に割当て(割当て解除)されている場合、Vは、Cのインスタンス・イニシャライザまたはインスタンス変数イニシャライザの前に割当て(割当て解除)されています。
Cをクラスとし、VをCのスーパークラスで宣言されたCの空のfinal
非static
メンバー・フィールドとします。その場合:
Vは、C内のコンストラクタの宣言の前に確実に割当て解除されます(さらに言えば、確実に割り当てられていません)。
Vは、Cのすべてのインスタンス・イニシャライザおよびインスタンス変数イニシャライザの前に確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。
Cをクラスとし、VをCの空のfinal
static
メンバー・フィールドとします。その場合:
- Vは、Cのすべてのコンストラクタ宣言、インスタンス・イニシャライザおよびインスタンス変数イニシャライザの前に確実に割り当てられます(さらに言えば、確実に割当て解除されません)。
Cをクラスとし、VをCのコンストラクタまたはインスタンス変数イニシャライザに含まれた文Sで宣言されたローカル変数とします。その場合:
- コンストラクタ宣言またはインスタンス変数イニシャライザの前に、Vは確実に割当て解除されます(さらに言えば、確実に割当て解除されていません)。
クラスCのコンストラクタ(8.8.7)内では、次のルールが適用されます:
代替コンストラクタ呼出し(8.8.7.1)の後に、Vは確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。
Vは、明示的または暗黙的なスーパークラス・コンストラクタ呼出し(8.8.7.1)の前に、確実に割当て解除されます(さらに言えば、確実に割り当てられていません)。
Cにインスタンス・イニシャライザまたはインスタンス変数イニシャライザがない場合、明示的または暗黙的なスーパークラス・コンストラクタ呼出しの後に、Vは確実に割り当てられません(さらに言えば、確実に割当て解除されています)。
Cに少なくとも1つのインスタンス・イニシャライザまたはインスタンス変数イニシャライザがある場合、VがCの右端のインスタンス・イニシャライザまたはインスタンス変数イニシャライザの後に割当て(割当て解除)されていると、Vは、明示的または暗黙的なスーパークラス・コンストラクタ呼出しの後に割当て(割当て解除)されます。
Vがコンストラクタ宣言の前に割当て(割当て解除)されると、Vは、コンストラクタ本体(8.8.7)のプロローグの前に割当て(割当て解除)されます。
Vがプロローグの前に割当て(割当て解除)されていると、Vは、空のプロローグの後に割当て(割当て解除)されます。
Vは、Vがプロローグ内の最後の文の後に割当て(割当て解除)される場合、空でないプロローグの後に割当て(割当て解除)されます。
Vは、Vがプロローグの後で割当て(割当て解除)されると、明示的なコンストラクタ呼出し(8.8.7.1)の前に割当て(割当て解除)されます。
Vは、Vが修飾子式の後に割当て(割当て解除)されると、修飾されたスーパー・コンストラクタ呼出しの引数リストの前に割当て(割当て解除)されます。
Vは、Vが呼出しの前に割当て(割当て解除)されていると別の明示的なコンストラクタ呼出しの引数リストの前に割当て(割当て解除)されます。
Vが引数リストの右端の引き数式の前に割当て(割当て解除)されているか、引数リストが空のときにVが引数リストの前に割当て(割当て解除)されている場合、Vは明示的なコンストラクタ呼出しの引数リストの後に割当て(割当て解除)されます。
Cのスーパークラスで宣言されたCの空の
final
非static
メンバー・フィールドは、スーパークラスのコンストラクタ呼出しの後に確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。その他の変数Vは、Vが呼出しの引数リストの後に割当て(割当て解除)されると、スーパークラスのコンストラクタ呼出しの後で割当て(割当て解除)されます。
CまたはCのスーパークラスで宣言されたCの空の
final
非static
メンバー・フィールドは、代替コンストラクタ呼出しの後に確実に割当てされます(さらに言えば、確実に割当て解除されていません)。その他の変数Vは、Vが呼出しの引数リストの後に割当て(割当て解除)されると、代替コンストラクタ呼出しの後で割当て(割当て解除)されます。
明示的なコンストラクタ呼出しがないコンストラクタ本体のエピローグの場合:
VがCの右端のインスタンス・イニシャライザまたはインスタンス変数イニシャライザの後に割当て(割当て解除)されるか、Cでインスタンス・イニシャライザまたはインスタンス変数イニシャライザを定義していないときにVがコンストラクタ本体のプロローグの後に割当て(割当て解除)される場合、Cで宣言されたCの空の
final
非static
メンバー・フィールドVはエピローグの前に割当て(割当て解除)されます。Cのスーパークラスで宣言されたCの空の
final
非static
メンバー・フィールドは、エピローグの前に確実に割り当てられています(さらに言えば、確実に割当て解除されていません)。その他の変数Vは、Vがコンストラクタ本体のプロローグの後に割当て(割当て解除)されている場合、エピローグの前に割当て(割当て解除)されます。
スーパークラス・コンストラクタ呼出しがあるコンストラクタ本体のエピローグの場合:
VがCの右端のインスタンス・イニシャライザまたはインスタンス変数イニシャライザの後に割当て(割当て解除)されるか、Cでインスタンス・イニシャライザまたはインスタンス変数イニシャライザを定義していないときにVがスーパークラス・コンストラクタ呼出しの後に割当て(割当て解除)される場合、Cで宣言されたCの空の
final
非static
メンバー・フィールドVはエピローグの前に割当て(割当て解除)されます。その他の変数Vは、Vがスーパークラス・コンストラクタ呼出しの後に割当て(割当て解除)されている場合、エピローグの前に割当て(割当て解除)されます。
代替コンストラクタ呼出しのあるコンストラクタのエピローグについては、Vが代替コンストラクタ呼出しの後に割当て(割当て解除)される場合、Vはエピローグの前に割当て(割当て解除)されます。
Vがエピローグの前に割当て(割当て解除)されていると、Vは、空のプロローグの後に割当て(割当て解除)されます。
Vは、Vがエピローグ内の最後の文の後に割当て(割当て解除)される場合、空でないエピローグの後に割当て(割当て解除)されます。
Vは、Vがプロローグまたはエピローグの前に割当て(割当て解除)されると、それぞれプロローグまたはエピローグの最初の文の前に割当て(割当て解除)されます。
Vは、VがプロローグまたはエピローグのSの直前の文の後に割当て(割当て解除)される場合、プロローグまたはエピローグの他の文Sの前に割当て(割当て解除)されます。
Vは、スーパー・コンストラクタ呼び出しの前にVが割当て(割当て解除)されると、スーパー・コンストラクタ呼び出しの修飾子式の前に割当て(割当て解除)されます。
Vは、Vが引数リストの前に割当て(割当て解除)されていると、明示的なコンストラクタ呼出しの左端の引き数式の前に割当て(割当て解除)されます。
Vは、Vがxの左への引き数式の後に割当て(割当て解除)されると、明示的コンストラクタ呼出しの他の引数式xの前に割当て(割当て解除)されます。
Cをクラスとし、VをCのスーパークラスで宣言されたCの空のfinal
メンバー・フィールドとします。その場合:
Vは、Cのコンストラクタまたはインスタンス・イニシャライザの本体であるブロックの前に確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。
Vは、Cのすべてのインスタンス変数イニシャライザの前に確実に割り当てられます(さらに言えば、確実に割当て解除されていません)。