4 無名クラスおよびインスタンスのmainメソッド

インスタンスのmainメソッド無名クラスをJava言語に追加すると、受講者は単一クラス・プログラムの合理化された宣言を記述し、後でプログラムをシームレスに拡張して、スキルの成長に合わせてより高度な機能を使用できます。

インスタンスのmainメソッドと無名クラスの2つのプレビュー機能がJava言語に追加されています。これは、受講者が大規模なプログラム用に設計されたすべての言語機能を理解しなくても小規模なプログラムの作成を開始できるようにする言語の漸進的なステップです。受講者は、独立した方言とはかけ離れたJava言語を使用して、単一クラス・プログラムの合理化された宣言を記述し、後で開始プログラムをシームレスに拡張して、スキルの成長に応じてより高度な機能を使用できるようになりました。Javaの熟練者は、Java言語の大規模プログラミングのスキャフォールディングを必要としない単純なJavaプログラムを記述する際に、インスタンスのmainメソッド無名クラス機能が役立つことに気づく場合もあります。

ノート:

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

インスタンスのmainメソッド無名クラスの背景情報については、JEP 445を参照してください。

Java言語を使用すると、開発チームは何年にもわたり、大規模で複雑なアプリケーションを作成、開発、保守できます。これは、データの非表示、再利用、アクセス制御、ネームスペース管理、モジュール性のための豊富な機能を備えたマルチパラダイム言語であり、コンポーネントを個別に開発および保守しながらクリーンに構成できます。これらの機能を使用すると、コンポーネントは、他のコンポーネントとの相互作用のために明確に定義されたインタフェースを公開したり、内部実装の詳細を非表示にして、それぞれを独立して進化させたりすることができます。オブジェクト指向パラダイム自体は、明確に定義されたプロトコルを介して相互作用する複数のピースをつなげ、実装の詳細の抽象化も行うように設計されています。この大規模コンポーネントの構成は、「大規模プログラミング」と呼ばれます。

Java言語には、すべてがコンポーネントの内部にある「小規模プログラミング」に役立つ多くの構文も用意されています。近年、データ指向プログラミングでは、モジュールとその小規模プログラミング機能によって大規模プログラミング機能が強化されました。

Java言語は、最初のプログラミング言語となることも意図されています。プログラマが開発チームの一員として最初に取りかかるときに大規模なプログラムは記述しません。小さなプログラムだけを記述します。様々なユーザーが作成したコンポーネントを個別に進化させるのに役立つカプセル化とネームスペースは不要です。プログラミングを教える場合、講師はまず、変数、制御フローおよびサブルーチンの基本的な小規模プログラミングの概念から始めます。その段階では、クラス、パッケージおよびモジュールの大規模プログラミングの概念は必要ありません。

Java受講者の最初のプログラムとして使用されることの多いクラシックなHello, World!プログラムについて考えてみます。
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
} 

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

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

プレビュー言語機能、インスタンスのmainメソッドおよび無名クラスは、プログラマがアクセス修飾子、static修飾子またはString[]パラメータを使用せずにプログラムを記述できるようにすることで、Hello, World!などの単純なプログラムを記述する複雑さを軽減します。講師は、大規模プログラミング構文の紹介を、必要になるまで延期できます。

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

新規のプログラマはコンピュータ・プログラムを作成して実行することを望みますが、現在のJava言語仕様では、クラスのコアJavaユニットと基本コンパイル・ユニット(つまり、パッケージ宣言で構成されたソース・ファイル)を定義した後、インポート宣言と1つ以上のクラス宣言を定義することに重点を置いています。

mainメソッドを含むクラスを選択して、その依存関係をモジュール・パスまたはクラス・パス(あるいはその両方)の形式でアセンブルし、クラスをロード、初期化し、引数を指定してmainメソッドを起動するアクションが、起動プロトコルを構成します。JDKでは、起動プロトコルはランチャによってjava実行可能ファイルとして実装されます。

インスタンスmainメソッドを許可することにより、新しいプレビュー言語機能によってJava起動プロトコルが強化され、次のようにプログラムのエントリ・ポイント宣言の柔軟性が向上します。
  • 起動されたクラスのmainメソッドに、publicprotectedまたはデフォルト(パッケージなど)のアクセスを許可します。
  • 起動されたクラスにString[]パラメータを持つstatic mainメソッドが含まれておらず、パラメータのないstatic mainメソッドが含まれている場合、起動プロトコルはそのメソッドを呼び出します。
  • 起動されたクラスにstatic mainメソッドが含まれておらず、private以外のゼロ・パラメータ・コンストラクタ(publicprotected、パッケージ・アクセスなど)およびprivate以外のインスタンスmainメソッドがある場合、起動プロトコルはクラスのインスタンスを構築します。クラスにString[]パラメータを持つインスタンスmainメソッドがある場合、起動プロトコルはそのメソッドを呼び出します。それ以外の場合は、パラメータなしでインスタンスmainメソッドを呼び出します。

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

mainメソッドの選択

これは、クラスの起動時の動作の変更です。

起動プロトコルは、次のメソッドのうち最初のメソッドを呼び出します。

  • 起動されたクラスで宣言された非プライベート・アクセス(publicprotected、パッケージなど)のstatic void main(String[] args)メソッド

  • 起動されたクラスで宣言された非プライベート・アクセスのstatic void main()メソッド
  • 起動されたクラスで宣言されたかスーパークラスから継承された非プライベート・アクセスのvoid main(String[] args)インスタンス・メソッド

  • 起動されたクラスで宣言されたかスーパークラスから継承された非プライベート・アクセスのvoid main()インスタンス・メソッド

ノート:

起動されたクラスがインスタンスmainメソッドを宣言する場合、スーパークラスで宣言された継承された"従来の"public static void main(String[] args)ではなく、そのメソッドが呼び出されます。したがって、起動されたクラスが"従来の"mainメソッドを継承するが、別のメソッド(インスタンスmainなど)が選択されている場合、JVMは実行時に標準エラーに警告を発行します。

選択したmainがインスタンス・メソッドで、内部クラスのメンバーである場合、プログラムは起動に失敗します。

無名クラス

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

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

クラスは、主要目的であるオブジェクトの構築用のテンプレートとして機能する前に、メソッドおよびフィールドの名前空間として機能します。次の場合に、クラスの概念に直面することを受講者に求めてはなりません。
  • 変数、制御フローおよびサブルーチンの基本的な構成要素に慣れる前
  • オブジェクト指向の学習を始める前
  • まだ単純な単一ファイル・プログラムを作成している場合。
すべてのメソッドがクラスに存在する場合でも、必要のないコードの明示的なクラス宣言の要求を停止できます。これは、必要のないコードの明示的なパッケージ宣言やモジュール宣言は必要ないためです。

Javaコンパイラは、クラス宣言で囲まれていないメソッドを含むソース・ファイルを検出すると、そのようなメソッド(および、ファイル内で宣言されている囲まれていないフィールドおよびクラス)を最上位の無名クラスのメンバーと暗黙的に見なします。

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

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

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

無名クラスは、明示的に宣言されたクラスとほぼ同じです。メンバーは同じ修飾子(privatestaticなど)を持つことができ、修飾子のデフォルトは同じです(packageアクセスやインスタンス・メンバーシップなど)。クラスには、static初期化子およびインスタンス初期化子を含めることができます。1つの重要な違いは、無名クラスにはデフォルトのゼロ・パラメータ・コンストラクタがあるものの、他のコンストラクタは持てないことです。

これらの変更により、Hello, World!を次のように記述できるようになりました。
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 HelloWorld.java

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

この時点で、無名クラスを持つJavaファイルのAPIドキュメントを生成するように求められた場合、javadocツールは失敗します。これは、無名クラスが他のクラスからアクセス可能なAPIを定義していないためです。この動作は将来のリリースで変更される可能性があります。

Class.isSyntheticメソッドは、無名クラスに対してtrueを返します。

プログラムの拡大

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

概念および構文は、必要に応じてプログラムによって無名クラスに追加できます。無名クラスは、明示的なクラス宣言内に宣言(import文を除く)をラップすることで、後で通常のクラスに簡単に進化できます。

mainメソッドの完全な排除が自然な次のステップのように思えるかもしれませんが、これは受講者の最初のJavaプログラムを大きなプログラムに正常に進化させ、明白でない制限を課すという目標に対して不利に作用します(JEP 445の「代替」の項を参照)。void修飾子を削除すると、同様に特有のJavaダイアレクトが作成されます。