名前のないクラスおよびインスタンスのmainメソッド(プレビュー)

Java®言語仕様バージョン21.0.2+10-LTS-54への変更

このドキュメントでは、Java SE 21プレビュー機能である名前のないクラスおよびインスタンスのmainメソッドをサポートするためのJava言語仕様に対する変更について説明します。この機能の概要は、JEP 445を参照してください。

コンパニオン・ドキュメントでは、名前のないクラスおよびインタフェースのmainメソッドをサポートするためのJava仮想マシン仕様に必要な変更について説明します。

変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。

変更ログ:

2023-05-30: 編集上のマイナー変更。

2023-05-24: 編集上のその他の変更。

2023-05-19: 12.1.4 JEPの変更を反映して、候補のmainメソッドの定義を変更します。

2023-05-15:

2023-05-02: 初稿リリース

第6章: 名前

6.7 完全修飾名および正規名

プリミティブ型、名前付きパッケージ、名前付きの最上位クラス、最上位インタフェースにはすべて次のような完全修飾名があります。

各メンバー・クラス、メンバー・インタフェース、および配列型には次のように完全修飾名を付けることができます

ローカル・クラス、ローカル・インタフェース、または匿名クラス、または名前のない最上位クラスに完全修飾名は付けられません。

すべてのプリミティブ型、名前付きパッケージ、名前付きの最上位クラス、および最上位インタフェースに正規名があります。

メンバー・クラス、メンバー・インタフェース、および配列型にはそれぞれ正規名を持たせることができます

ローカル・クラス、ローカル・インタフェース、または匿名クラス、または名前のない最上位クラスに正規名は付けられません。

例6.7-1.完全修飾名

コードでは、次の点に注意してください。

package points;
class Point    { int x, y; }
class PointVec { Point[] vec; }

Pointの完全修飾名は「points.Point」、型PointVecの完全修飾名は「points.PointVec」、クラスPointVecのフィールドvecの完全修飾名は「points.Point[]」です。

例6.7-2.完全修飾名および正規名

完全修飾名と正規名の違いは、コードでは次のように示されます。

package p;
class O1 { class I {} }
class O2 extends O1 {}

p.O1.Ip.O2.Iはどちらもメンバー・クラスIを示す完全修飾名ですが、p.O1.Iのみがその正規名です。

第7章: パッケージおよびモジュール

7.3 コンパイル・ユニット

CompilationUnitは、Javaプログラムの構文文法(2.3)の目標シンボル(2.1)です。これは、次のプロダクションによって定義されます。

CompilationUnit:
OrdinaryCompilationUnit
UnnamedClassCompilationUnit
ModularCompilationUnit
OrdinaryCompilationUnit:
[PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
UnnamedClassCompilationUnit:
{ImportDeclaration} {ClassMemberDeclarationNoMethod} MethodDeclaration {ClassMemberDeclaration}
ClassMemberDeclarationNoMethod:
FieldDeclaration
ClassDeclaration
InterfaceDeclaration
;
ModularCompilationUnit:
{ImportDeclaration} ModuleDeclaration

通常コンパイル・ユニットは3つの部分で構成され、それぞれがオプションです。

名前のないクラスのコンパイル・ユニットは、次のもので構成されます。

名前のないクラスのコンパイル・ユニットは、次のプロパティを満たすクラスを暗黙的に宣言します。

このクラスのすべてのメンバー(暗黙的に宣言されたメンバーを含む)は、クラス内のメンバー宣言に関する通常のルールの対象になります。

このクラスが候補のmainメソッド(12.1.4)を宣言しない場合、コンパイル時エラーが発生します。

名前のないパッケージには、メンバーとして複数の名前のないクラスがある場合があります。

モジュラ・コンパイル・ユニットmodule宣言(7.7)で構成され、オプションで、その前にimport宣言が含められます。import宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースのstaticメンバーを、module宣言内でそれらの単純名を使用して参照できます。

package宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;が出現する場合と同様に、それぞれのコンパイル・ユニットによって、事前定義済のパッケージjava.langで宣言されたそれぞれのpublicクラスまたはインタフェースが暗黙的にインポートされます。その結果、それらのクラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。

ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージjavaとそのサブパッケージlangおよびio内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。

それぞれの観察可能なコンパイル・ユニットは、次のようにモジュールに関連付けることができます。

コンパイル・ユニットの可観測性はそのパッケージの可観測性(7.4.3)に影響し、可観測のコンパイル・ユニットとモジュールとの関連付けはそのモジュールの可観測性(7.7.6)に影響します。

モジュールMに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットをコンパイルする場合、ホスト・システムは、Mの宣言で指定された依存を順守する必要があります。具体的には、ホスト・システムは、本来なら観察可能な通常コンパイル・ユニットを、Mから見えるもののみに制限する必要があります。Mから見える通常コンパイル・ユニットは、Mによって読み取られるモジュールに関連付けられた、観察可能な通常コンパイル・ユニットです。Mによって読み取られるモジュールは、java.lang.moduleパッケージ仕様に記載されているように、Mを唯一のルート・モジュールとする解決の結果によって指定されます。ホスト・システムは、解決を実行して、Mによって読み取られるモジュールを特定する必要があります。java.lang.moduleパッケージ仕様に記載されているいずれかの理由で解決が失敗すると、コンパイル時にエラーが発生します。

読取り可能性の関係は再帰的であるため、Mはそれ自体を読み取り、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットはすべて、Mから見えます。

Mによって読み取られるモジュールによって、Mから一意に見えるパッケージが決まり(7.4.3)、さらに、それによって、スコープ内の最上位パッケージと、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニット内のコードのパッケージ名の意味の両方が決まります(6.36.5.36.5.5)。

前述のルールによって、モジュラ・コンパイル・ユニット内の注釈(特に、モジュール宣言に適用される注釈)で使用されるパッケージと型の名前が、モジュールに関連付けられた通常コンパイル・ユニットにそれらが出現する場合と同様に解釈されることが確実になります。

異なる通常コンパイル・ユニットで宣言されたクラスおよびインタフェースは、相互を循環的に参照することがあります。Javaコンパイラは、そのようなすべてのクラスおよびインタフェースを同時にコンパイルするように準備する必要があります。

第8章: クラス

クラス宣言では、新規クラスを定義し、その実装方法について記述します(8.1)。

最上位クラス(7.6)は、コンパイル・ユニットで直接宣言されたクラスです。

ネストしたクラスは、宣言が別のクラスまたはインタフェース宣言の本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.59.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。

ネストしたクラスの一部の種類は内部クラス(8.1.3)であり、これは、包含クラス・インスタンス、ローカル変数および型変数を参照できるクラスです。

enumクラス(8.9)は、名前付きクラス・インスタンスの小さいセットを定義する省略構文で宣言されたクラスです。

レコード・クラス(8.10)は、値の単純集計を定義する省略構文で宣言されたクラスです。

非常に小さなプログラムや日常的な開発では、最上位クラスを正規名も完全修飾名(6.7)もないunnamedにできます。名前のないクラスは明示的には宣言されませんが、名前のないクラスのコンパイル・ユニット(7.3)によって暗黙的に宣言されます。名前のない最上位クラスは、プログラムの初期クラス(12.1.4)にできますが、それ自体を含むソース・コードの名前で参照することはできません。

この章では、すべてのクラスに共通するセマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。

クラスが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)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子publicprotectedまたは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 クラス宣言

クラス宣言では、クラスを指定します。

3種類のクラス宣言があります: 標準クラス宣言enum宣言(8.9)およびレコード宣言(8.10)。

ClassDeclaration:
NormalClassDeclaration
EnumDeclaration
RecordDeclaration
NormalClassDeclaration:
{ClassModifier} class TypeIdentifier [TypeParameters]
[ClassExtends] [ClassImplements] [ClassPermits] ClassBody

クラスは、名前のないクラスのコンプライアンス・ユニット(7.3)、クラス・インスタンス作成式(15.9.5)、およびクラス本体で終了するenum定数(8.9.1)によっても暗黙的に宣言されます。

クラス宣言内のTypeIdentifierは、クラスの名前を指定します。

クラスの単純名が前後のクラスまたはインタフェースの単純名と同じである場合は、コンパイル時にエラーが発生します。

クラス宣言のスコープおよびシャドウ化については、6.3および6.4.1に規定されています。

第9章 インタフェース

インタフェース宣言は、1つ以上のクラスによって実装できる新しいインタフェースを定義します。プログラムはインタフェースを使用して、そうしない場合は関連しないクラスに共通のサブタイプを提供でき、関連するクラスが共通のabstractスーパークラスを共有する必要をなくします。

インタフェースにはインスタンス変数がなく、通常は1つ以上のabstractメソッドを宣言し、そうでない場合、そのabstractメソッドの実装を指定すると、関連しないクラスによってインタフェースを実装できます。インタフェースを直接インスタンス化することはできません。

最上位インタフェース(7.6)は、コンパイル・ユニットで直接宣言されたインタフェースです。

ネストしたインタフェースは、宣言が別のクラスまたはインタフェース宣言の本体内で行われるインタフェースです。ネストされたインタフェースはメンバー・インタフェース(8.59.5)またはローカル・インタフェース(14.3)にできます。

注釈インタフェース(9.6)は、注釈 (9.7)の反射型の表現によって実装することを意図した、特別な構文で宣言されるインタフェースです。

この章では、すべてのインタフェースに共通のセマンティクスについて説明します。特定の種類のインタフェースに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。

インタフェースは他の1つ以上のインタフェースの直接拡張になるように宣言できます。つまり、それがオーバーライドまたは非表示にできるメンバーを除き、拡張するインタフェースのすべてのメンバー・クラスおよびインタフェース、インスタンス・メソッド、およびstaticフィールドを継承します。

クラスは、1つ以上のインタフェースを直接実装するように宣言できます(8.1.5)。これは、インタフェースによって指定されたすべてのabstractメソッドをそのクラスのインスタンスが実装することを意味します。クラスは必然的に、その直接スーパークラスおよび直接スーパーインタフェースが実装するすべてのインタフェースを実装します。こうした(複数の)インタフェース継承によって、オブジェクトは、スーパークラスを共有することなく、(複数の)共通動作をサポートできます。

クラスとは異なり、インタフェースをfinalとして宣言することはできません。ただし、インタフェースはsealed (9.1.1.4)と宣言してサブクラスおよびサブインタフェースを制限できます。

宣言された型がインタフェース型である変数は、指定されたインタフェースを実装するクラスのインスタンスへの参照をその値として持つことができます。インタフェースのすべてのabstractメソッドがクラスに実装されていても十分ではありません。クラス、またはそのスーパークラスのいずれかが、実際に、インタフェースを実装するように宣言されている必要があります。そうでない場合、クラスはインタフェースを実装するとみなされません。

クラスとは異なり、名前のない最上位インタフェース(7.3)を暗黙的に宣言することはできません。

第12章: 実行

この章では、プログラムの実行時に発生するアクティビティについて説明します。これは、Java仮想マシンのライフ・サイクルと、プログラムを形成するクラス、インタフェースおよびオブジェクトのライフ・サイクルに基づいて構成されています。

Java仮想マシンは、指定されたクラスまたはインタフェースをロードしてから、この指定されたクラスまたはインタフェースでメソッドmainを呼び出して起動します。セクション12.1では、この章の概念の導入として、mainの実行に関連するロード、リンクおよび初期化のステップの概要を示します。その他のセクションでは、ロード(12.2)、リンク(12.3)、および初期化(12.4)について詳しく説明します。

この章では、新しいクラス・インスタンスを作成する手順(12.5)と、クラス・インスタンスを終了する手順(12.6)について引き続き説明します。最後に、クラスのアンロード(12.7)と、プログラム終了時に実行する手順(12.8)について説明します。

12.1 Java仮想マシンの起動

Java仮想マシンは、指定されたクラスまたはインタフェースのメソッドmainを呼び出して起動します。このmainメソッドに仮パラメータがある場合は、文字列の1つの配列である単一の引数が渡されます渡しますこの仕様の例では、この最初のクラスは通常、Testと呼ばれます。

Java仮想マシン起動の正確なセマンティクスは、Java仮想マシン仕様、Java SE 21 Editionの第5章に記載されています。ここでは、Javaプログラミング言語の視点から、このプロセスの概要を示します。

初期クラスまたはインタフェースをJava仮想マシンに指定する方法は、この仕様の範囲を超えていますが、コマンドラインを使用するホスト環境では一般的であり、コマンドライン引数として指定されるinitialクラスまたはインタフェースの完全修飾名、およびメソッドmainの引数に指定される文字列として使用される次のコマンドライン引数に使用されます。元のコンパイル・ユニットが名前のないクラスのコンパイル・ユニット(7.3)であった場合、コンパイル・ユニットを含むファイルの名前は通常、初期クラスまたはインタフェースの名前の指定に使用されます。

たとえば、UNIX実装では、コマンドラインに次のように入力します。

java Test reboot Bob Dot Enzo

通常は、クラスTest (名前のないパッケージ内のクラス)のメソッドmainを呼び出してJava仮想マシンを起動し、「reboot」、「Bob」、「Dot」および「Enzo」の4つの文字列を含む引数配列を渡します。

一方、ファイルHelloWorld.javaに次の名前のないクラスのコンパイル・ユニットが含まれていた場合:

void main() {
    System.out.println("Hello, World!");
}

コンパイル後のコマンドラインは次のようになります。

java HelloWorld

通常は、暗黙的に宣言された名前のないクラス(7.3)のmainメソッドを呼び出してJava仮想マシンを起動すると、次のような出力が得られます。

Hello, World!

ここでは、Test初期クラスまたはインタフェースを実行するためにJava仮想マシンが実行するステップの概要を説明します。ロード、リンクおよび初期化プロセスの例については、後のセクションで詳しく説明します。

12.1.1 初期クラスまたはインタフェースTestのロード

初期クラスまたはインタフェースTestメソッドmainを最初に実行しようとすると、クラスTestそれがロードされていない、つまりJava仮想マシンには現在、このクラスまたはインタフェースのバイナリ表現が含まれていないことが検出されます。次に、Java仮想マシンがクラス・ローダーを使用して、このようなバイナリ表現を見つけようとします。この処理に失敗した場合は、エラーがスローされます。このロード・プロセスについては、12.2で詳しく説明します。

12.1.2 初期クラスまたはインタフェースTestのリンク: 検証、準備、(オプションの)解決

クラスまたはインタフェースTestがロードされたら、メソッドmainを呼び出す前に初期化する必要があります。また、Testは、すべてのクラスやインタフェースと同様に、これを初期化する前にリンクする必要があります。リンクには、検証、準備および(オプションの)解決が含まれます。リンクの詳細は、12.3を参照してください。

検証では、クラスまたはインタフェースTestのロードされた表現が適切なシンボル・テーブルで整形式になっていることを確認します。検証では、クラスまたはインタフェースTestを実装するコードが、Javaプログラミング言語およびJava仮想マシンのセマンティック要件に従っていることもチェックされます。検証中に問題が検出されると、エラーがスローされます。検証の詳細は、12.3.1を参照してください。

準備には、静的ストレージと、Java仮想マシンの実装によって内部的に使用されるデータ構造(メソッド表など)の割当てが含まれます。準備の詳細は、12.3.2を参照してください。

解決とは、前述の他のクラスおよびインタフェースをロードし、参照が正しいことを確認することによって、クラスまたはインタフェースTestから他のクラスおよびインタフェースへのシンボリック参照をチェックするプロセスです。

解決の手順は、初期リンケージの時点ではオプションです。実装では、再帰的に参照されるクラスおよびインタフェースからすべてのシンボリック参照を解決するポイントに至るまで、非常に早期にリンクされているクラスまたはインタフェースからシンボリック参照を解決できます。(この解決により、これらのロードおよびリンクの手順でエラーが発生する可能性があります。)この実装の選択肢は、C言語の単純な実装で長年使用されてきた「静的」リンクに似ています。(これらの実装では、コンパイルされたプログラムは通常、プログラムで使用されるライブラリ・ルーチンへの完全に解決されたリンクを含む、プログラムの完全なリンク・バージョンのある「a.out」ファイルとして表されます。これらのライブラリ・ルーチンのコピーは、「a.out」ファイルに含まれています。)

かわりに、実装では、シンボリック参照がアクティブに使用されている場合にのみ解決するように選択されます。この戦略のすべてのシンボリック参照に対する一貫した使用は、「最も遅い」解決になります。この場合、クラスまたはインタフェースTestに別のクラスへのシンボリック参照が複数ある場合、プログラムの実行中にそれらの参照が使用されるたびに1つずつ解決され、まったく使用されない場合は何も解決されません。

解決が実行される際の唯一の要件は、解決中に検出されたエラーが、プログラムのある時点でスローされることです。そこでは、エラーに関与するクラスまたはインタフェースへの直接的または間接的なリンクが必要なプログラムにより、アクションが実行されます。前述の「静的」な例の実装の選択肢を使用すると、初期クラスまたはインタフェースTestで示されているクラスまたはインタフェース、あるいはそれ以降の再帰的に参照されるクラスおよびインタフェースに関わるプログラムが実行される前に、ロードおよびリンクのエラーが発生する可能性があります。「最も遅い」解決を実装したシステムでは、不正なシンボリック参照がアクティブに使用されている場合にのみ、これらのエラーがスローされます。

解決プロセスの詳細は、12.3.3を参照してください。

12.1.3 初期クラスまたはインタフェーステスト: イニシャライザの実行

続く例では、Java仮想マシンは引き続き初期クラスまたはインタフェースTestメソッドmainを実行しようとしています。これは、クラスが初期化されている場合のみ可能です(12.4.1)。

初期化は、初期クラスまたはインタフェースTestのクラス変数イニシャライザおよび静的イニシャライザのテキスト順の実行で構成されます。ただし、それTestを初期化する前に、その直接スーパークラスだけでなく、その直接スーパークラスの直接スーパークラスも再帰的に初期化する必要があります。最も単純なケースでは、初期クラスまたはインタフェースTestは暗黙的な直接スーパークラスとしてObjectを持ちます。クラスObjectがまだ初期化されていない場合は、初期クラスまたはインタフェースTestの初期化より前に初期化する必要があります。クラスObjectにはスーパークラスがないため、再帰はここで終了します。

初期クラスまたはインタフェースTestのスーパークラスとして別のクラスSuperがある場合、初期クラスまたはインタフェースTestの前にSuperを初期化する必要があります。これには、(まだ実行していない場合は) Superのロード、検証および準備が必要です。また、実装によっては、Superなどのシンボリック参照の再帰的な解決も含まれる場合があります。

したがって、初期化によって、ロード、リンク、および初期化エラーが発生する可能性があります。これには他のクラスやインタフェースに関連するエラーも含まれます。

初期化プロセスの詳細は、12.4を参照してください。

12.1.4 Test.mainmainメソッドの起動

最後に、初期クラスまたはインタフェースTestの初期化が完了した後(この間に、ロード、リンク、および初期化という他の結果が得られる場合があります)、初期クラスまたはインタフェースTestmainメソッドmainが呼び出されます。

メソッドmainは、publicstaticおよびvoidとして宣言する必要があります。宣言型がStringの配列である仮パラメータ(8.4.1)を指定します。したがって、次のいずれかの宣言を使用できます。

public static void main(String[] args)

public static void main(String... args)

初期クラスまたはインタフェースのメソッドは、mainという名前で次のいずれかが該当する場合、candidateです。

候補のmainメソッドには、throws句(8.4.6)がある場合があります。

mainメソッドで許可されるシグネチャは、Java SE 21で大幅に拡張されました。それ以前は、mainのシグネチャで可能な唯一のバリエーションは、単一の仮パラメータの型におけるString...に対するString[]でした。Java SE 21以上では、mainは12のシグネチャ(6つのstaticと6つの非static)のいずれかを持つことができます。String[]が単一の仮パラメータの型でString...と区別されている場合、この数は18に増加します。

初期クラスまたはインタフェースがメンバー間で複数の候補mainメソッドをカウントする場合、コンパイル時エラーにはなりません

staticmainメソッドを継承できるため、クラスまたはインタフェースにmainメソッドが存在することはすぐには明らかになりません。たとえば、インタフェースのデフォルト・メソッドはインスタンス・メソッド(9.4)であるため、インタフェースを実装するクラスによって継承される候補となる場合があります。開発ツールでは、プログラムの開始を示すメンバーmainメソッドがクラスまたはインタフェースにある場合に強調表示されます。

Java SE 21で動作が変更され、継承されたstatic mainメソッドは候補メソッドとは見なされなくなりました。mainメソッドのみがstaticであり、継承されている既存の初期クラスまたはインタフェースは、引き続きプログラムの開始を示すようにリファクタリングする必要があります。

次のルールを適用する場合と同様に、初期クラスまたはインタフェースのmainメソッドが呼び出されます。

呼び出される候補メソッドがない場合、またはインスタンス候補メソッドの呼出し時に初期クラスに適切なコンストラクタがない場合の実装の動作は、この仕様の範囲を超えています。

第13章: バイナリ互換性

13.1 バイナリの形式

プログラムは、Java仮想マシン仕様、Java SE 20 Editionによって規定されているclassファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。

クラスまたはインタフェース宣言に対応するclassファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。

  1. クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。

    • 名前付きの最上位クラスまたはインタフェース(7.6)のバイナリ名はその正規名(6.7)です。名前のない最上位クラスのバイナリ名(7.3)は任意の有効な識別子(3.8)です。

      コンパイル・ユニットがファイルに格納されるJava SEプラットフォームの単純な実装では、名前のない最上位のバイナリ名は通常、名前のない最上位クラスのコンパイル・ユニット(7.3)から拡張子(.java.javなど)を除いたファイル名になります。

    • メンバー・クラスまたはインタフェース(8.59.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く$とメンバーの単純名で構成されます。

    • ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに$、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。

    • 匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く$、および空でない数字のシーケンスで構成されます。

    • 汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.29.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く$、型変数の単純名で構成されます。

    • 汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く$、メソッドの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

    • 汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く$、コンストラクタの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。

  2. 別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。

  3. 定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。

    このようなフィールドがstaticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。

    このようなフィールドが非staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstaticフィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。

  4. クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾クラスまたはインタフェースを次のように定義します。

    • 式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、QCにします。そうでない場合、Qは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qは参照の修飾クラスまたはインタフェースです。

    • 参照の形式がTypeName.fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースは参照のクラスまたはインタフェースの修飾型です。

    • 式の形式がExpressionName.fまたはPrimary.fである場合は次のようになります。

      • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、参照の修飾クラスまたはインタフェースはV1のイレイジャ(4.6)です。

      • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャが参照の修飾クラスまたはインタフェースです。

    • 式の形式がsuper.fの場合、Cのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    • 式の形式がTypeName.super.fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    fへの参照は、参照の修飾クラスまたはインタフェースおよびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。

    また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。

  5. クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾クラスまたはインタフェースを次のように定義します。

    • DObjectである場合、メソッド呼出しの修飾クラスまたはインタフェースはObjectです。

    • それ以外の場合:

      • メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、QCにします。そうでない場合、Qは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qはメソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がTypeName.mまたはReferenceType::mの場合、TypeNameによって示されるクラスまたはインタフェース、またはReferenceTypeのイレイジャは、メソッド呼び出しの修飾クラスまたはインタフェースです。

      • 式の形式がExpressionName.mPrimary.mExpressionName::mまたはPrimary::mである場合、次のようになります。

        • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vnである場合、メソッド呼出しの修飾クラスまたはインタフェースはV1のイレイジャです。

        • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャがメソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がsuper.mまたはsuper::mの場合、Cのスーパークラスは、メソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がTypeName.super.mまたはTypeName.super::mであり、TypeNameがクラスXを示している場合、Xのスーパークラスがメソッド呼出しの修飾クラスまたはインタフェースです。また、TypeNameがインタフェースXを示している場合、Xがメソッド呼出しの修飾クラスまたはインタフェースです。

    メソッドへの参照は、コンパイル時に、メソッド呼出しの修飾クラスまたはインタフェースと、メソッドの宣言されたシグネチャ(8.4.2)のイレイジャへのシンボリック参照に解決される必要があります。メソッドのシグネチャには、15.12.3で確認されたとおりに次のすべてが含まれている必要があります。

    • メソッドの単純名

    • メソッドに対するパラメータ数

    • 各パラメータの型へのシンボリック参照

    また、メソッドへの参照には、示されるメソッドの戻り型のイレイジャへのシンボリック参照が含まれているか、示されるメソッドがvoidを宣言され、値を戻さないことを示すものが含まれている必要があります。

  6. クラス・インスタンス作成式(15.9)、明示的なコンストラクタ呼出し文(8.8.7.1)、またはクラスまたはインタフェースC内のClassType :: new (15.13)形式のメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているコンストラクタmを参照している場合、Oracleでは、コンストラクタ呼出しの修飾クラスを次のように定義します。

    • 式の形式がnew D(...)ExpressionName.new D(...)Primary.new D(...)またはD :: newの場合、コンストラクタ呼出しの修飾クラスはDです。

    • 式の形式がnew D(...){...}ExpressionName.new D(...){...}またはPrimary.new D(...){...}の場合、コンストラクタ呼出しの修飾クラスは式によって宣言される匿名クラスです。

    • 式の形式がsuper(...)ExpressionName.super(...)またはPrimary.super(...)の場合、コンストラクタ呼出しの修飾クラスはCの直接スーパークラスです。

    • 式がthis(...)の形式の場合、コンストラクタ呼出しの修飾クラスはCです。

    コンストラクタへの参照は、コンパイル時に、コンストラクタ呼出しの修飾クラスとコンストラクタの宣言されたシグネチャ(8.8.2)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。

    • コンストラクタのパラメータ数

    • 各仮パラメータの型へのシンボリック参照

クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。

  1. これがObjectではなく、クラスである場合、このクラスの直接スーパークラスへのシンボリック参照。

  2. 各直接スーパーインタフェースへのシンボリック参照(ある場合)。

  3. フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。

  4. これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。

  5. クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。

  6. クラスまたはインタフェースを実装するために必要なコード。

    • インタフェースについては、フィールド・イニシャライザのコード、およびブロック本体を持つ各メソッドの実装(9.4.3)。

    • クラスについては、フィールド・イニシャライザ、インスタンス・イニシャライザおよび静的イニシャライザのコード、ブロック本体を持つ各メソッドの実装(8.4.7)、および各コンストラクタの実装。

  7. すべてのクラスまたはインタフェースに、その正規名(6.7)をリカバリするために十分な情報が含まれている必要があります。

  8. すべてのメンバー・クラスまたはインタフェースに、ソースレベルのアクセス修飾子をリカバリするために十分な情報が含まれている必要があります(6.6)。

  9. すべてのネストしたクラスまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。

  10. すべてのクラスまたはインタフェースに、そのすべてのメンバー・クラスおよびインタフェース(8.59.5)へのシンボリック参照、およびその本体内で宣言されたネストした他のすべてのクラスおよびインタフェースへのシンボリック参照が含まれている必要があります。

  11. Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり(JVMS §2.9)、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります。これには、2つの例外があります。

    1. クラス初期化メソッド(JVMS§2.9)は、ソース・コードで明示的または暗黙的に宣言された単一のメソッドには対応しませんが、クラスの静的イニシャライザ(8.7)またはstaticフィールド・イニシャライザ(8.3.2)、あるいはその両方に対応します。このため、クラス初期化メソッドは合成としてマークできません。

    2. 名前のないクラスのバイナリ表現は合成としてマークする必要があります。

  12. Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.18.8.98.9.315.9.5.1)。

次の仮パラメータは、ソース・コードで暗黙的に宣言されます。

参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、classファイル内で必須としてマークできるのは仮パラメータおよびモジュールのみであるため、必須としてはマークされません(JVMS §4.7.24、JVMS §4.7.25)。

モジュール宣言に対応するclassファイルには、バイナリ名がmodule-infoであり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclassファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。

次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に対して加えられる可能性のある変更点について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclassファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclassファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。