5 暗黙的に宣言されたクラスおよびインスタンスのmainメソッド

JEP463によってJava言語に「インスタンスのmainメソッド」と「暗黙的に宣言されたクラス」の2つのプレビュー機能が追加されたことで、受講者は大規模なプログラム用に設計されたすべての言語機能を理解しなくても、最初のプログラムを作成できます。

ノート:

これはプレビュー機能です。プレビュー機能は、設計、仕様および実装が完了したが、永続的でない機能です。プレビュー機能は、将来のJava SEリリースで、異なる形式で存在することもあれば、まったく存在しないこともあります。プレビュー機能が含まれているコードをコンパイルして実行するには、追加のコマンド行オプションを指定する必要があります。『Preview Language and VM Features』を参照してください。

インスタンスのmainメソッドおよび暗黙的に宣言されたクラスの背景情報は、JEP 463を参照してください。

Javaプログラミング言語は、何年にもわたり大規模なチームによって開発および保守される、大規模で複雑なアプリケーションの開発に優れています。データの非表示、再利用、アクセス制御、ネームスペース管理、モジュール性のための豊富な機能を備えており、コンポーネントを個別に開発および保守しながらクリーンに構成できます。その大規模コンポーネントの構成は、大規模プログラミングと呼ばれます。

ただし、Javaプログラミング言語は最初の言語になることも意図しており、小規模プログラミング(コンポーネントの内部にあるすべてのもの)に役立つ多くの構文を提供しています。プログラマが最初に取りかかるとき、大規模なプログラムをチームで記述するのではなく、小規模なプログラムを自分で記述します。この段階では、クラス、パッケージおよびモジュールの大規模プログラミングの概念は必要ありません。

プログラミングを教える場合、講師はまず、変数、制御フローおよびサブルーチンの基本的な小規模プログラミングの概念から始めます。クラス、パッケージおよびモジュールの大規模プログラミングの概念は必要ありません。プログラミングを学習している受講者は、様々なユーザーが作成したコンポーネントを個別に進化させるのに後で役立つカプセル化とネームスペースは不要です。

小規模プログラミングのためのJavaプログラミング言語のサポートを強化するために、JEP 463では、次のことを実現する、インスタンスのmainメソッドおよび暗黙的に宣言されたクラスの2つのプレビュー機能が追加されています。
  • staticではなく、publicである必要がなく、String[]パラメータを必要としないインスタンスのmainメソッドを許可することで、Javaプログラムが起動されるプロトコルを強化します。「フレキシブル起動プロトコル」を参照してください。
  • コンパイル・ユニット(ソース・ファイル)がクラスを暗黙的に宣言できるようにします。Java言語仕様のパッケージおよびモジュールに関する章のコンパイル・ユニットに関する項、および「暗黙的に宣言されたクラス」を参照してください。
Java受講者の最初のプログラムとして使用されることの多いクラシックなHelloWorldプログラムについて考えてみます。
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
} 

この最初のプログラムでは:

  • class宣言および必須のpublicアクセス修飾子は、大規模プログラミング構文です。外部コンポーネントへの明確に定義されたインタフェースを使用してコード・ユニットをカプセル化する場合に役立ちますが、この小さな例では意味がありません。
  • コードと外部コンポーネント(この場合はオペレーティング・システムのシェル)のインタフェースをとるためのString[] argsパラメータも存在します。特に、HelloWorldのような単純なプログラムでは使用されないため、ここでは不可解で役に立ちません。
  • static修飾子は、Javaのクラスとオブジェクト・モデルの一部です。初心者にとって、staticは不可解なだけでなく有害です。mainから呼び出して使用できるメソッドまたはフィールドをさらに追加するために、受講者はこれらすべてをstaticとして宣言するか(一般的な習慣でもよい習慣でもないイディオムを伝播します)、静的メンバーとインスタンス・メンバーの違いに直面し、オブジェクトをインスタンス化する方法について学習する必要があります。
新しいプログラマは、変数や制御フローについて学習する前、そして大規模プログラムを適切に整理しておくための大規模プログラミング構文の有用性を理解する前に、これらの大規模プログラミング構文に遭遇します。講師は多くの場合、忠告します。「それについては心配しないでください。後で理解することになるので。」これは彼らとその受講者たちを納得させるものではありません。Java言語が非常に複雑であるという根強い印象を受講者に残します。

JEP 463のプレビュー言語機能、インスタンスのmainメソッドおよび暗黙的に宣言されたクラスは、プログラマがアクセス修飾子、static修飾子またはString[]パラメータを使用せずにプログラムを記述できるようにすることで、HelloWorldなどの単純なプログラムを記述する複雑さを軽減します。受講者は、独立した方言とはかけ離れたJava言語を使用して、単一クラス・プログラムの合理化された宣言を記述し、後で開始プログラムをシームレスに拡張して、スキルの成長に応じてより高度な機能を含めることができるようになりました。Javaの熟練者は、Java言語の大規模プログラミングのスキャフォールディングを必要としない単純なJavaプログラムを記述する際に、インスタンスのmainメソッドと暗黙的に宣言されたクラスが便利な機能であることに気づく場合もあります。講師は、大規模プログラミング構文の紹介を、必要になるまで延期できます。

フレキシブル起動プロトコル

JDKでは、起動プロトコルはコマンドライン・ツールによってjava実行可能ファイルとして実装されます。

mainメソッドを含むクラスを選択して、その依存関係をモジュール・パスまたはクラス・パス(あるいはその両方)の形式でアセンブルし、クラスをロード、初期化し、引数を指定してmainメソッドを起動するアクションが、起動プロトコルを構成します。JEP 463では、起動プロトコルが強化され、次のように、プログラムのエントリ・ポイントの宣言で柔軟性が向上し、特にインスタンスのmainメソッドを許可するようになりました。

  • 起動されたクラスのmainメソッドに、publicprotectedまたはデフォルト(パッケージなど)のアクセスを許可します。
  • 起動されたクラスにString[]パラメータを持つmainメソッドが含まれている場合は、そのメソッドが選択されます。
  • 起動されたクラスにパラメータのないmainメソッドが含まれている場合は、そのメソッドが選択されます。
  • いずれの場合も、選択されたmainメソッドがstaticであれば、それが呼び出されます。
  • それ以外の場合、選択されたmainメソッドはインスタンスのmainメソッドであり、起動されたクラスには、ゼロ・パラメータの、private以外のコンストラクタ(publicprotectedまたはパッケージ・アクセス)が必要です。そのコンストラクタを呼び出してから、結果のオブジェクトのmainメソッドを呼び出します。そのようなコンストラクタがない場合は、エラーを報告して終了します。

  • 適切なmainメソッドがない場合は、エラーを報告して終了します。

インスタンスのmainメソッドを使用すると、「暗黙的に宣言されたクラスおよびインスタンスのmainメソッド」に示されているHelloWorldプログラムを次のように簡略化できます。
class HelloWorld { 
    void main() { 
        System.out.println("Hello, World!");
    }
}

暗黙的に宣言されたクラス

Java言語では、すべてのクラスがパッケージ内にあり、すべてのパッケージがモジュール内にあります。これらのネームスペースおよびカプセル化の構文は、すべてのコードに適用されます。ただし、それらを必要としない小さなプログラムでは省略できます。

クラス・ネームスペースを必要としないプログラムは、package文を省略して、そのクラスを無名パッケージの暗黙的なメンバーにすることができます。無名パッケージ内のクラスは、名前付きパッケージのクラスから明示的に参照できません。パッケージをカプセル化する必要がないプログラムは、モジュール宣言を省略して、そのパッケージを無名モジュールの暗黙的なメンバーにすることができます。無名モジュール内のパッケージは、名前付きモジュール内のパッケージから明示的に参照できません。

クラスは、主要目的であるオブジェクトの構築用のテンプレートとして機能する前に、メソッドおよびフィールドの名前空間として機能します。次の場合に、クラスの概念に直面することを受講者に求めてはなりません。

  • 変数、制御フローおよびサブルーチンの基本的な構成要素に慣れる前

  • オブジェクト指向の学習を始める前

  • まだ単純な単一ファイル・プログラムを作成している場合。

すべてのメソッドがクラスに存在する場合でも、必要のないコードの明示的なクラス宣言の要求を停止できます。これは、必要のないコードの明示的なパッケージ宣言やモジュール宣言は必要ないためです。

JEP 463以降、Javaコンパイラは、クラス宣言で囲まれていないメソッドを含むソース・ファイルを検出すると、そのメソッド、同様のメソッド、およびファイル内で囲まれていないフィールドおよびクラスを、暗黙的に宣言された最上位のクラスの本体を形成するものと見なします。

暗黙的に宣言されたクラス(暗黙的なクラスとも呼ばれる)は常に無名パッケージのメンバーです。また、finalでもあり、インタフェースを実装したり、Object以外のクラスを拡張したりすることはありません。暗黙的なクラスは名前で参照できないため、その静的メソッドへのメソッド参照はありません。ただし、thisキーワードは引き続き使用でき、インスタンス・メソッドへのメソッド参照も使用できます。

暗黙的なクラスのコードは、暗黙的なクラスを名前で参照できないため、暗黙的なクラスのインスタンスは直接構築できません。このようなクラスは、スタンドアロン・プログラムとして、またはプログラムのエントリ・ポイントとしてのみ役立ちます。したがって、暗黙的なクラスには、「フレキシブル起動プロトコル」で説明されているように起動できるmainメソッドが必要です。この要件は、Javaコンパイラによって適用されます。

暗黙的なクラスは無名パッケージに存在し、無名パッケージは無名モジュールに存在します。1つの無名パッケージのみ(複数のクラス・ローダーがない場合)と1つの無名モジュールのみが存在できますが、無名モジュールには複数の暗黙的なクラスが存在できます。すべての暗黙的なクラスは、mainメソッドを含み、プログラムを表します。したがって、無名パッケージ内の複数の暗黙的なクラスが、複数のプログラムを表します。

暗黙的なクラスは、明示的に宣言されたクラスと類似しています。メンバーは同じ修飾子(privatestaticなど)を持つことができ、修飾子のデフォルトは同じです(packageアクセスやインスタンス・メンバーシップなど)。1つの重要な違いは、暗黙的なクラスにはデフォルトのゼロ・パラメータ・コンストラクタがあるものの、他のコンストラクタは持てないことです。

これらの変更により、HelloWorldプログラムを次のように記述できるようになりました。
void main() {
    System.out.println("Hello, World!");
}
最上位のメンバーは暗黙的なクラスのメンバーとして解釈されるため、プログラムは次のように記述することもできます。
String greeting() { return "Hello, World!"; }

void main() {
    System.out.println(greeting());
}
または、フィールドを使用して、プログラムを次のように記述できます。
String greeting = "Hello, World!";

void main() {
    System.out.println(greeting);
}

暗黙的なクラスを含むHelloWorld.javaという名前のソース・ファイルは、次のようにjavaコマンドライン・ツールを使用して起動できます。
$ java HelloWorld.java

Javaコンパイラは、そのファイルを起動可能なクラス・ファイルHelloWorld.classにコンパイルします。この場合、コンパイラはクラス名にHelloWorldを実装詳細として選択します。ただし、依然としてこの名前をJavaソース・コードで直接使用することはできません。

この時点で、javadocツールは暗黙的なクラスのAPIドキュメントを生成できません。これは、暗黙的なクラスが他のクラスからアクセス可能なAPIを定義していないためです。ただし、暗黙的なクラスのフィールドおよびメソッドは、APIドキュメントを生成できます。

プログラムの拡大

不要な概念と構文を省略することによって、暗黙的なクラスとして記述されたHelloWorldプログラムは、プログラムが実際に実行する処理により重点を置きます。それでも、すべてのメンバーは通常のクラス内にあるように解釈され続けています。

概念および構文は、必要に応じてプログラムによって暗黙的なクラスに簡単に追加できます。暗黙的なクラスを通常のクラスに進化させるには、import文を除く宣言を明示的なclass宣言内にラップするだけで済みます。