このドキュメントでは、Java SE 22のプレビュー機能である文字列テンプレートをサポートするためのJava言語仕様の変更について説明します。この機能の概要は、JEP 459を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2023-11-14: (7.3)メンバーSTRのみが(RAWではない)すべてのコンパイル・ユニットによって暗黙的にインポートされることを明確にしました。
2023-10-13: (15.8.6)テンプレート式の型付けを変更して、対応するprocessメソッドの戻り型を使用するようにしました。
2023-10-03: 初稿をリリースしました。主な変更点:
- (13.1)フィールドへの参照を必ずシンボルによる参照にコンパイルする必要をなくし、
StringTemplateのインポートしたメンバーへの参照に関する例外を追加しました。(これにより、コンパイラで、テンプレート・プロセッサがSTRまたはRAWである場合に、インタフェースStringTemplateのロードも初期化も発生しないようにテンプレート式を最適化できるようになりました。)
第2章: 文法
2.1 コンテキスト自由文法
コンテキスト自由文法は、多数のプロダクションで構成されます。各プロダクションには、左側に非終端と呼ばれる抽象記号があり、右側に1つ以上の非終端および終端記号のシーケンスがあります。文法ごとに、指定したアルファベットから終端記号が描画されます。
目標シンボルと呼ばれる単一の特徴のある非終端文字で構成される文から始まるコンテキスト自由文法では、言語、つまり、シーケンス内の非終端文字を、非終端が左側になるプロダクションの右側と繰り返し置き換えることから得られる、終端記号の一連のシーケンスを指定します。
一部の文法はあいまいです。つまり、目標シンボルから始まり、終端記号の同じシーケンスで終わるプロダクションを適用する方法は複数あります。あいまいさを解決するには、すべての代替に対してプロダクションを適用するという特定の方法を1つ選ぶか、他のコンテキスト情報を考慮に入れる必要があります。
2.2 字句文法
Javaプログラミング言語の字句文法は3で紹介します。この文法は、終端記号としてUnicode文字セットの文字を使用しています。これは、Unicode文字のシーケンス(3.1)が入力要素のシーケンス(3.2)にどのように変換されるかを表す、目標シンボルInput (3.5)から始まる一連のプロダクションを定義するものです。
空白(3.6)とコメント(3.7)を破棄したこれらの入力要素は、Javaプログラミング言語の構文文法の終端記号を形成し、トークン(3.5)と呼ばれます。これらのトークンは、Javaプログラミング言語の識別子(3.8)、キーワード(3.9)、リテラル(3.10)、セパレータ(3.11)、および演算子(3.12)を含みます。
字句文法はあいまいであり、このようなあいまいさを解決する方法(3.5)はいくつかの規則によって決まります。
2.3 構文文法
Javaプログラミング言語の構文文法については、第4章、6章から10章、14章、および15章を参照してください。この文法には、終端記号として字句文法で定義されたトークンがあります。目標シンボルCompilationUnit (7.3)から始まる一連のプロダクションを定義し、トークンのシーケンスが構文的に正しいプログラムを形成する方法を説明します。
一部の場所では、構文文法の特定のプロダクションが、字句文法(3.5)のあいまいさを解決するためのコンテキストを提供します。
便宜上、構文文法は第19章にまとめられています。
第2章の他の部分は変更されていません。
第3章: 字句構造
この章では、Javaプログラミング言語の字句構造について説明します。
プログラムはUnicode (3.1)で記述されますが、ASCII文字のみを使用するUnicode文字を含めるためにUnicodeエスケープ(3.3)を使用できるように、字句変換(3.2)が可能です。行の終了文字は、一貫した行番号を維持しながら既存のホスト・システムの各種規則をサポートできるように定義(3.4)されています。
字句変換によって生成されるUnicode文字は、空白(3.6)、コメント(3.7)、およびトークンである入力要素(3.5)のシーケンスに還元されます。トークンは、構文文法の識別子(3.8)、キーワード(3.9)、リテラル(3.10)、セパレータ(3.11)、および演算子(3.12)、およびフラグメント(3.13)です。
3.1 Unicode
プログラムはUnicode文字セット(1.7)を使用して記述されます。この文字セットとその関連文字エンコーディングに関する情報については、<https://www.unicode.org/>をご覧ください。
Java SEプラットフォームでは、Unicode Standardが展開されるたびに追跡されます。特定のリリースで使用されるUnicodeの正確なバージョンは、クラスCharacterのドキュメントで指定されます。
Unicode Standardは、本来は固定幅の16ビット文字エンコーディングとして設計されました。その後、16ビット以上の表現を必要とする文字を許容するように変更されています。有効なコード・ポイントの範囲は現在、16進数のU+n表記でU+0000からU+10FFFFです。コード・ポイントがU+FFFFよりも大きい文字は補助文字と呼ばれます。16ビット単位のみを使用して文字の範囲全体を表すために、Unicode StandardではUTF-16というエンコーディングを定義しています。このエンコーディングでは、補足文字は16ビット・コード・ユニットのペアとして表現され、最初のコード・ユニットは上位サロゲート範囲(U+D800からU+DBFF)から、2番目のコード・ユニットは下位サロゲート範囲(U+DC00からU+DFFF)から取得されます。U+0000からU+FFFFの範囲の文字の場合、コード・ポイントとUTF-16コード・ユニットの値は同じです。
Javaプログラミング言語は、UTF-16エンコーディングを使用した16ビット・コード単位のシーケンスでテキストを表します。
Java SEプラットフォームの一部のAPIは、主に
Characterクラスにあり、32ビット整数を使用してコード・ポイントを個々のエンティティとして表します。Java SEプラットフォームには、16ビット表現と32ビット表現を変換するメソッドが用意されています。
この仕様では、表現が関連しているコード・ポイントおよびUTF-16コード・ユニット、そして表現がディスカッションに関連しない汎用用語文字を使用します。
コメント(3.7)、識別子(3.8)、および文字リテラルの内容、文字列リテラル、およびテキスト・ブロックおよびテンプレート(3.10.4、3.10.5、3.10.6、3.13)を除き、プログラム内のすべての入力要素(3.5)は、ASCII文字(またはASCII文字になるUnicodeエスケープ(3.3))からのみ形成されます。
ASCII (ANSI X3.4)は、情報交換用米国標準コード(American Standard Code for Information Interchange)です。Unicode UTF-16エンコーディングの最初の128文字はASCII文字です。
3.5 入力要素およびトークン
Unicodeエスケープ処理(3.3)の結果の入力文字および行終了文字と入力行認識(3.4)が入力要素のシーケンスに削減されます。
- Input:
- {InputElement} [Sub]
- InputElement:
- WhiteSpace
- Comment
- Token
- Token:
- Identifier
- Keyword
- Literal
- Separator
- Operator
- Fragment
- Sub:
- ASCII SUB文字、別名はcontrol-Z
空白またはコメント以外の入力要素はトークンです。トークンは構文文法(2.3)の終端記号です。
空白(3.6)およびコメント(3.7)は、隣接している場合に別の方法でトークン化される可能性があるトークンを区切るために役立ちます。
たとえば、入力文字
-および=は、間に空白またはコメントがない場合にのみ、演算子トークン-=(3.12)を形成できます。別の例では、10文字の入力文字staticvoidは単一の識別子トークンを形成しますが、11文字の入力文字static void(ASCII SP文字がcとvの間にある)は空白で区切られたキーワード・トークンstaticとvoidのペアを形成します。
特定のオペレーティング・システムとの互換性のための特別な便宜的措置として、ASCII SUB文字(\u001aまたはcontrol-Z)は、エスケープされた入力ストリームの最後の文字である場合は無視されます。
Inputプロダクションはあいまいです。つまり、入力文字の一部のシーケンスには、入力文字を入力要素に還元する(つまり、入力文字をトークン化する)方法が複数あります。あいまいさは次のように解決されます。
識別子トークンまたはリテラル・トークンのいずれかに還元できる入力文字のシーケンスは、常にリテラル・トークンに還元されます。
識別子トークンまたは予約キーワード・トークン(3.9)のいずれかに還元できる入力文字のシーケンスは、常に予約キーワード・トークンに還元されます。
コンテキスト・キーワード・トークンまたはその他の(キーワード以外の)トークンに還元できる入力文字のシーケンスは、3.9で指定されているように、コンテキストに従って還元されます。
入力文字
>が型コンテキスト(4.11)、つまり、構文文法のTypeまたはUnannType (4.1、8.3)の一部として出現すると、隣接する>文字との組合せによって別の演算子を形成する場合でも、常に数値比較演算子>に還元されます。>文字のこのルールがないと、List<List<String>>などの型に含まれる2つの連続する>カッコは符号付き右シフト演算子>>としてトークン化されます。一方、List<List<List<String>>>などの型に含まれる3つの連続する>カッコは符号なし右シフト演算子>>>としてトークン化されます。さらに悪いことに、List<List<List<List<String>>>>などの型に含まれる4つ以上の連続する>カッコのトークン化はあいまいになります。>、>>および>>>トークンの様々な組合せが>>>>文字を表すためです。
結果の入力ストリーム内の2つのトークンxおよびyについて考えます。xがyより先行する場合、xはyの左側にあり、yはxの右側にあると言い表します。
たとえば、次の簡単なコードでは、
class Empty { }
}トークンは、この2次元表現では{トークンの左下に示されていますが、{トークンの右側にあると言い表します。左と右という言葉の使用に関するこの取り決めにより、たとえば、バイナリ演算子の右側のオペランドや代入の左側と言い表すことができます。
3.10 リテラル
3.10.7 エスケープ・シーケンス
文字リテラル、文字列リテラル、およびテキスト・ブロック、およびテンプレートのフラグメント(3.10.4、3.10.5、3.10.6、3.13)内で、エスケープ・シーケンスにより、Unicodeエスケープ(3.3)を使用せずに、一部のグラフィック以外の文字をはじめ、一重引用符、二重引用符、バックスラッシュ文字を表現できます。
- EscapeSequence:
-
\ b* (バックスペースBS、Unicode\u0008)* -
\ s* (スペースSP、Unicode\u0020)* -
\ t* (水平タブHT、Unicode\u0009)* -
\ n* (改行LF、Unicode\u000a)* -
\ f* (フォーム・フィードFF、Unicode\u000c)* -
\ r* (復帰CR、Unicode\u000d)* -
\LineTerminator * (行の継続、Unicode表現なし)* -
\ "* (二重引用符"、Unicode\u0022)* -
\ '* (一重引用符'、Unicode\u0027)* -
\ \* (バックスラッシュ\、Unicode\u005c)* -
OctalEscape * (8進数、Unicode
\u0000~\u00ff)* - OctalEscape:
-
\OctalDigit -
\OctalDigit OctalDigit -
\ZeroToThree OctalDigit OctalDigit # - OctalDigit:
- (次のうちの1つ)
-
0 1 2 3 4 5 6 7 - ZeroToThree:
- (次のうちの1つ)
-
0 1 2 3
前述のOctalDigitプロダクションは3.10.1で得ています。8進数のエスケープは、Cとの互換性のために用意されていますが、Unicode値
\u0000~\u00FFしか表現できないため、通常はUnicodeエスケープの方が優先されます。
エスケープ・シーケンス内でバックスラッシュに続く文字がLineTerminatorまたはASCII b、s、t、n、f、r、"、'、\、0、1、2、3、4、5、6、7ではない場合、コンパイル時にエラーが発生します。
文字リテラル、文字列リテラル、またはテキスト・ブロック、またはテンプレートのフラグメント内のエスケープ・シーケンスは、\および末尾の文字を、EscapeSequence文法のUnicodeエスケープが示す単一文字に置き換えることによって解釈されます。行の継続エスケープ・シーケンスには、対応するUnicodeエスケープがないため、これは何にも置き換えられずに解釈されます。
行の継続エスケープ・シーケンスは、テキスト・ブロック内には出現する可能性がありますが、文字リテラルまたは、文字列リテラルまたは文字列テンプレートのフラグメントでは、LineTerminatorが許可されないため、出現できません。
文字シーケンス\{はエスケープ・シーケンスではありませんが、テンプレート(3.13)に出現する際に特別な意味を持ちます。
3.13 フラグメント
テンプレート(15.8.6)は、文字列リテラルまたはテキスト・ブロックに似ていますが、1つ以上の埋込み式が含まれます。埋込み式とは、先頭に文字シーケンス\{、文末に文字}が付加された式です。
フラグメントは、テンプレートの式以外の部分を表します。
- Fragment:
- StringTemplateBegin
- StringTemplateMid
- StringTemplateEnd
- TextBlockTemplateBegin
- TextBlockTemplateMid
- TextBlockTemplateEnd
- StringTemplateBegin:
-
"StringFragment\{ - StringTemplateMid:
-
}StringFragment\{ - StringTemplateEnd:
-
}StringFragment" - StringFragment:
- { StringCharacter }
- TextBlockTemplateBegin:
-
"""{ TextBlockWhiteSpace } LineTerminator TextBlockFragment\{ - TextBlockTemplateMid:
-
}TextBlockFragment\{ - TextBlockTemplateEnd:
-
}TextBlockFragment""" - TextBlockFragment:
- { TextBlockCharacter }
便宜上、ここでは3.10.5および3.10.6の次のプロダクションを示します。
- StringCharacter:
- InputCharacter (ただし、
"または\ではない)- EscapeSequence
- TextBlockWhiteSpace:
- WhiteSpace (ただし、LineTerminatorではない)
- LineTerminator:
- 「改行」とも呼ばれるASCII LF文字
- 「復帰」とも呼ばれるASCII CR文字
- ASCII LF文字が後に続くASCII CR文字
- TextBlockCharacter:
- InputCharacter (ただし、
\ではない)- EscapeSequence
- LineTerminator
フラグメントのcontentは次のように定義されます。
- StringTemplateBeginの内容は、左側の
"の直後から始まり、最初のシーケンス\{の直前に終わる文字のシーケンスです。(シーケンス\{は有効なエスケープ・シーケンスではないため、最初の埋込み式の前に付加されます。) - StringTemplateMidの内容は、文字
}の直後から始まり、シーケンス\{の2回目の直前に終わる文字のシーケンスです。 - StringTemplateEndの内容は、文字
}の直後から始まり、右側の"の直前に終わる文字のシーケンスです。 - TextBlockTemplateBeginの内容は、左側のデリミタ(3.10.6)の直後から始まり、シーケンス
\{の初回の直前に終わる文字のシーケンスです。(シーケンス\{は有効なエスケープ・シーケンスではないため、最初の埋込み式の前に付加されます。) - TextBlockTemplateMidの内容は、文字
}の直後から始まり、シーケンス\{の2回目の直前に終わる文字のシーケンスです。 - TextBlockTemplateEndの内容は、文字
}の直後から始まり、右側のデリミタ(3.10.6)の直前に終わる文字のシーケンスです。
これは、StringTemplateBegin、StringTemplateMid、またはStringTemplateEndトークンの内容に行の終終了文字(3.4)が表示されるときのコンパイル時のエラーです。
TextBlockTemplateBegin、TextBlockTemplateMid、またはTextBlockTemplateEndトークンの内容は、次の手順を適用するとさらに変換されます。
行の終了文字は、次のようにASCII LF文字に正規化されます。
ASCII LF文字が後に続くASCII CR文字は、ASCII LF文字に翻訳されます。
ASCII CR文字は、ASCII LF文字に翻訳されます。
テンプレートは文字列リテラル(およびテキスト・ブロック)に似ていますが、入力文字のシーケンスが構文的に正しい文字列リテラルと構文的に正しいテンプレートの両方を形成できないという意味では、あいまいではありません。これは、テンプレートには少なくとも1つの埋込み式が含まれている必要がありますが、埋込み式の先頭に付けるシーケンス\{が、文字列リテラル(またはテキスト・ブロック)の有効なエスケープ・シーケンスではないためです。
ただし、フラグメント・プロダクションでは、それ以外のトークン・プロダクション(3.5)によるあいまいさが発生します。このようなあいまいさは次のように解決されます。
入力文字の入力要素への還元(3.5)の際に、StringTemplateMid (またはStringTemplateEnd)と概念的に一致する入力文字のシーケンスは、最初の入力文字
}がテンプレートの埋込み式に現れるClassBody、ConstructorBody、EnumBody、RecordBody、InterfaceBody、ElementValueArrayInitializer、ArrayInitializer、Block、またはSwitchBlock (8.1.7、8.8.7、8.9.1、8.10.2、9.1.5、9.7.1、10.6、14.2、14.11.1)の終端として認識されるコンテキストにない場合のみ、StringTemplateMid (またはStringTemplateEnd)に還元されます。入力文字の入力要素への還元(3.5)の際に、TextBlockTemplateMid (またはTextBlockTemplateEnd)と概念的に一致する入力文字のシーケンスは、最初の入力文字
}がテンプレートの埋込み式に現れるClassBody、ConstructorBody、EnumBody、RecordBody、InterfaceBody、ElementValueArrayInitializer、ArrayInitializer、Block、またはSwitchBlockの終端として認識されるコンテキストにない場合のみ、TextBlockTemplateEnd (またはTextBlockTemplateEnd)に還元されます。
たとえば、18文字の入力文字のシーケンス
" \ { n e w i n t [ ] { 4 2 } } "を考えてみます。最初の3つの入力文字がStringTemplateBeginに還元されます。次の12個の入力文字は、トークンKeyword (new)、Keyword (int)、Separator ([)、Separator (])、Separator ({)、およびLiteral (42)に還元されます。シーケンス内の次の入力文字}は、あいまいさを生じさせます。これはSeparatorに還元するか、次の}および"入力文字とともにStringTemplateEndに還元できます。構文文法では配列作成式のArrayInitializer(15.10.1)のコンテキストが提供されるため、前述のルールにより、入力文字}がSeparatorに還元されます。残りの}および"の入力文字は、StringTemplateEndに還元されます。
第7章: パッケージおよびモジュール
7.3 コンパイル・ユニット
CompilationUnitは、Javaプログラムの構文文法(2.3)の目標シンボル(2.1)です。これは、次のプロダクションによって定義されます。
- CompilationUnit:
- OrdinaryCompilationUnit
- ModularCompilationUnit
- OrdinaryCompilationUnit:
- [PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
- ModularCompilationUnit:
- {ImportDeclaration} ModuleDeclaration
通常コンパイル・ユニットは3つの部分で構成され、それぞれがオプションです。
コンパイル・ユニットが属するパッケージの完全修飾名(6.7)を指定する、
package宣言(7.4)。package宣言がないコンパイル・ユニットは、名前のないパッケージ(7.4.2)に属します。他のパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースの
staticメンバーを、それらの単純名を使用して参照できるようにするimport宣言(7.5)。クラスおよびインタフェース(7.6)の最上位宣言。
モジュラ・コンパイル・ユニットはmodule宣言(7.7)で構成され、オプションで、その前にimport宣言が含められます。import宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内のクラスおよびインタフェースや、クラスおよびインタフェースのstaticメンバーを、module宣言内でそれらの単純名を使用して参照できます。
すべてのコンパイル単位は暗黙的に次のものをインポートします。
package宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;が出現するものとして、事前定義済パッケージjava.langで宣言されているそれぞれのpublicクラスまたはインタフェース。
package宣言の直後の各コンパイル・ユニットの先頭に宣言import static java.lang.StringTemplate.STR;が出現するものとして、事前定義済インタフェースStringTemplateで宣言されている静的メンバーSTR。
その結果、暗黙的にインポートされたそれらすべてのクラスおよびインタフェースクラス、インタフェースおよび静的フィールドの名前をそれぞれのコンパイル・ユニットで単純名として使用できます。
ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージjavaとそのサブパッケージlangおよびio内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。
セクション7.3の他の部分は変更されていません。
7.5 インポート宣言
7.5.3 単一静的インポート宣言
単一静的インポート宣言では、特定の単純名を持つアクセス可能なすべてのstaticメンバーをクラスまたはインタフェースからインポートします。これにより、単一静的インポート宣言が出現するコンパイル・ユニットのモジュール、クラスおよびインタフェース宣言内でstaticメンバーを単一名で使用できるようになります。
- SingleStaticImportDeclaration:
-
importstaticTypeName.Identifier;
TypeNameは、クラスまたはインタフェースの正規名(6.7)である必要があります。
クラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。
名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。
Identifierは、名前付きクラスまたはインタフェースの少なくとも1つのstaticメンバーに名前を付ける必要があります。その名前を持つstaticメンバーがないか、名前付きメンバーがすべてアクセス不可能である場合は、コンパイル時にエラーが発生します。
1つの単一静的インポート宣言で、同じ名前を持つ複数のフィールド、クラスまたはインタフェース、あるいは同じ名前とシグネチャを持つ複数のメソッドをインポートできます。このことが発生するのは、名前付きクラスまたはインタフェースが、すべて同じ名前を持つ複数のフィールド、メンバー・クラス、メンバー・インタフェースまたはメソッドをそれ自体のスーパー・タイプから継承する場合です。
これは単一静的インポート宣言で、すでに暗黙的にインポートされているstaticメンバーの冗長インポートに使用できます。
同じコンパイル・ユニット内の2つの単一静的インポート宣言が同じ単純名のクラスまたはインタフェースをインポートしようとすると、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。
単純名がxであるクラスまたはインタフェースが単一静的インポート宣言によってインポートされ、単純名がxである最上位クラスまたはインタフェース(7.6)もコンパイル・ユニットで宣言される場合、コンパイル時にエラーが発生します。
単純名がxであるクラスまたはインタフェースをインポートする単一静的インポート宣言と、単純名がxであるクラスまたはインタフェースをインポートする単一型インポート宣言(7.5.1)の両方がコンパイル・ユニットに含まれる場合、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。
7.5.4 オンデマンド静的インポート宣言
オンデマンド静的インポート宣言を使用すると、名前付きクラスまたはインタフェースのアクセス可能なすべてのstaticメンバーを必要に応じてインポートできます。
- StaticImportOnDemandDeclaration:
-
importstaticTypeName.*;
TypeNameは、クラスまたはインタフェースの正規名(6.7)である必要があります。
クラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。
名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。
これはオンデマンド静的インポート宣言で、すでに暗黙的にインポートされているstaticメンバーの冗長インポートに使用できます。
同じコンパイル・ユニット内の2つ以上のオンデマンド静的インポート宣言によって、同じクラスまたはインタフェースの名前が指定されることがあります。この場合、そのような宣言が1つのみ存在する場合と同様の結果になります。
セクション7.5.4の他の部分は変更されていません。
第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)を実装するクラスの新しいインスタンスの作成が必要になる場合があります。
- テンプレート式(15.8.6)の評価では、関数インタフェース型
StringTemplateを実装するクラスの新しいインスタンスの作成が必要になる場合があります。
このようなそれぞれの状況は、クラス・インスタンスの作成プロセスの一部として、指定された引数(ない場合もある)で呼び出される特定のコンストラクタ(8.8)を識別します。
新しいクラス・インスタンスが作成されるときは、常に、クラスで宣言されたすべてのインスタンス変数とクラスの各スーパークラスで宣言されたすべてのインスタンス変数(隠すことができるすべてのインスタンス変数を含む)のための空き領域を確保して、新しいクラス・インスタンスにメモリー領域が割り当てられます(8.3)。
オブジェクトのメモリーを割り当てるための空き領域が不十分である場合、OutOfMemoryErrorによってクラス・インスタンスの作成が突然完了します。それ以外の場合は、スーパークラスで宣言された変数を含め、新しいオブジェクトのすべてのインスタンス変数がデフォルト値(4.12.5)に初期化されます。
新しく作成されたオブジェクトへの参照が結果として返される直前に、指定されたコンストラクタが処理され、次の手順に従って新しいオブジェクトが初期化されます。
コンストラクタの引数を、このコンストラクタ呼出しのために新しく作成されたパラメータ変数に割り当てます。
このコンストラクタが(
thisを使用して)同一クラス内の別のコンストラクタの明示的なコンストラクタ呼出し(8.8.7.1)で始まる場合、その引数を評価し、この同じ5つのステップを使用して、コンストラクタの再帰的な呼出しを処理します。コンストラクタの呼出しが突然完了する場合、この手順は同じ理由で突然完了します。そうでない場合は、ステップ5から続行します。このコンストラクタは、(
thisを使用する)同一クラス内の別のコンストラクタの明示的なコンストラクタ呼出しからは始まりません。このコンストラクタがObject以外のクラス用の場合、このコンストラクタは、(superを使用する)スーパークラス・コンストラクタの明示的または暗黙的な呼出しから始まります。これら5つのステップを使用して、スーパークラス・コンストラクタを再帰的に呼び出す引数とプロセスを評価します。コンストラクタの呼出しが突然完了する場合、この手順は同じ理由で突然完了します。それ以外の場合は、ステップ4に進みます。このクラスのインスタンス・イニシャライザおよびインスタンス変数イニシャライザを実行し、インスタンス変数イニシャライザの値を対応するインスタンス変数に左から右へ順に割り当て、クラスのソース・コードにテキストで表示されるようにします。これらのイニシャライザのいずれかを実行して例外が発生した場合、それ以上のイニシャライザは処理されず、この手順が同じ例外で突然完了します。それ以外の場合は、ステップ5に進みます。
このコンストラクタの残りの本体を実行します。例外が突然完了する場合、この手順は同じ理由で突然完了します。それ以外の場合、この手順は正常に完了します。
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が出力されます。
第13章: バイナリ互換性
13.1 バイナリの形式
プログラムは、Java仮想マシン仕様、Java SE 22 Editionによって規定されているclassファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。
クラスまたはインタフェース宣言に対応するclassファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。
クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。
メンバー・クラスまたはインタフェース(8.5、9.5)のバイナリ名は、直前と直後のクラスまたはインタフェースのバイナリ名と、その後に続く
$とメンバーの単純名で構成されます。ローカル・クラスまたはインタフェース(14.3)のバイナリ名の構成は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後ろに
$、空でない数字のシーケンス、ローカル・クラスの単純名が続きます。匿名クラス(15.9.5)のバイナリ名は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名と、その後に続く
$、および空でない数字のシーケンスで構成されます。汎用クラスまたはインタフェースによって宣言される型変数のバイナリ名(8.1.2、9.1.2)は、それを直接囲んでいるクラスまたはインタフェースのバイナリ名、その後ろに順に続く
$、型変数の単純名で構成されます。汎用メソッド(8.4.4)で宣言された型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名と、その後に続く
$、メソッドの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。汎用コンストラクタ(8.8.4)で宣言された型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名と、その後に続く
$、コンストラクタの記述子(JVMS §4.3.3)、$、および型変数の単純名で構成されます。
別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。
定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。
このようなフィールドが
staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。このようなフィールドが非
staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstaticフィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。クラスC内のフィールド・アクセスを示す正当な式であるとすると、(i)定数値ではなく、(ii)
StringTemplateの暗黙的にインポートされたメンバーではなく(7.3)、(iii)クラスまたはインタフェースD内(場合によっては個別)で宣言されている、fという名前のフィールドを参照している場合、Oracleでは、フィールド参照の修飾クラスまたはインタフェースを次のように定義します。式が単純名によって参照されているときに、fが現在のクラスまたはインタフェースCのメンバーである場合、QをCにします。そうでない場合、Qは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qは参照の修飾クラスまたはインタフェースです。
参照の形式がTypeName
.fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースは参照のクラスまたはインタフェースの修飾型です。式の形式がExpressionName
.fまたはPrimary.fである場合は次のようになります。式の形式が
super.fの場合、Cのスーパークラスは、参照の修飾クラスまたはインタフェースです。式の形式がTypeName
.super.fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾クラスまたはインタフェースです。
fへの参照は、参照の修飾クラスまたはインタフェースおよびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。
また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。
クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾クラスまたはインタフェースを次のように定義します。
Dが
Objectである場合、メソッド呼出しの修飾クラスまたはインタフェースはObjectです。それ以外の場合:
メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、QをCにします。そうでない場合、Qは、mがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言にします。いずれの場合も、Qはメソッド呼出しの修飾クラスまたはインタフェースです。
式の形式がTypeName
.mまたはReferenceType::mの場合、TypeNameによって示されるクラスまたはインタフェース、またはReferenceTypeのイレイジャは、メソッド呼び出しの修飾クラスまたはインタフェースです。式の形式がExpressionName
.m、Primary.m、ExpressionName::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を宣言され、値を戻さないことを示すものが含まれている必要があります。クラス・インスタンス作成式(15.9)、明示的なコンストラクタ呼出し文(8.8.7.1)、またはクラスまたはインタフェースC内のClassType
::new(15.13)形式のメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているコンストラクタmを参照している場合、Oracleでは、コンストラクタ呼出しの修飾クラスを次のように定義します。式の形式が
newD(...)、ExpressionName.newD(...)、Primary.newD(...)またはD::newの場合、コンストラクタ呼出しの修飾クラスはDです。式の形式が
newD(...){...}、ExpressionName.newD(...){...}またはPrimary.newD(...){...}の場合、コンストラクタ呼出しの修飾クラスは式によって宣言される匿名クラスです。式の形式が
super(...)、ExpressionName.super(...)またはPrimary.super(...)の場合、コンストラクタ呼出しの修飾クラスはCの直接スーパークラスです。式が
this(...)の形式の場合、コンストラクタ呼出しの修飾クラスはCです。
コンストラクタへの参照は、コンパイル時に、コンストラクタ呼出しの修飾クラスとコンストラクタの宣言されたシグネチャ(8.8.2)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。
コンストラクタのパラメータ数
各仮パラメータの型へのシンボリック参照
13.1項の残りの部分に変更はありません。
第15章: 式
15.8 Primary式
Primary式には、最も単純な種類の式のほとんどが含まれ、ここからリテラル、オブジェクト作成、フィールド・アクセス、メソッド呼出し、メソッド参照、および配列アクセス、およびテンプレート式など、他のすべての式が作成されます。カッコ付きの式も、構文的にはPrimary式として扱われます。
- Primary:
- PrimaryNoNewArray
- ArrayCreationExpression
- PrimaryNoNewArray:
- Literal
- ClassLiteral
-
this -
TypeName
.this -
(Expression) - ClassInstanceCreationExpression
- FieldAccess
- ArrayAccess
- MethodInvocation
- MethodReference
- TemplateExpression
Javaプログラミング言語の文法のこの部分は、2つの点が異例です。まず、ローカル変数やメソッド・パラメータの名前などの単純な名前はPrimary式と想定されます。技術的な理由から、名前は、接尾辞式が導入された少し後でPrimary式とともにグループ化されます(15.14)。
この技術的な理由は、トークン・ルックアヘッドが1つのみのJavaプログラムの左から右への解析が可能なことに関係します。式
(z[3])および(z[])について考えてみます。1つ目はカッコで囲まれた配列アクセス(15.10.3)で、2つ目はキャストの先頭(15.16)です。先読み記号が[の時点で、左から右への解析によってzが終端ではない名前に還元されます。キャストのコンテキストでは、名前をPrimaryに還元する必要はありませんが、NameがPrimaryの代替の1つであった場合は、[の後に続くトークンに2つのトークンを先読みせず、還元する必要があるのか(つまり、現在の状況がカッコで囲まれた配列アクセスかキャストになるか)を判断できません。ここに示した文法では、NameとPrimaryを分離したままにし、他の特定の構文ルール(ClassInstanceCreationExpression、MethodInvocation、ArrayAccess、およびPostfixExpression向けのルール、識別子を直接使用するためFieldAccess、向けではない)で許可することによって問題を回避します。この戦略により、NameをPrimaryとして処理するかどうかの問題は、さらに多くのコンテキストを調査できるようになるまで実質的に保留にされます。
2つ目の異常な特徴は、式"
new int[3][3]"の潜在的な文法上のあいまいさが回避されることです。これは、Javaでは常に多次元配列の単一作成を意味しますが、文法的な適性がない場合、"(new int[3])[3]"と同じ意味であると解釈されることもあります。
このあいまいさは、Primaryに想定される定義をPrimaryおよびPrimaryNoNewArrayに分割すると排除できます。(これは、"dangling else"の問題を回避するためにStatementをStatementとStatementNoShortIf (14.5)に分割する場合と比較できます。)
15.8.1 字句リテラル
リテラル(3.10)は、変化しない固定の値を示します。
便宜上、ここでは3.10の次のプロダクションを示します。
- Literal:
- IntegerLiteral
- FloatingPointLiteral
- BooleanLiteral
- CharacterLiteral
- StringLiteral
- TextBlock
- NullLiteral
リテラルの型は次のように決定されます。
Lまたはl(ell)で終わる整数リテラル(3.10.1)の型はlong(4.2.1)です。その他の整数リテラルの型は
int(4.2.1)です。Fまたはfで終わる浮動小数点リテラル(3.10.2)の型はfloat(4.2.3)です。その他の浮動小数点リテラルの型は
double(4.2.3)です。
整数リテラル、浮動小数点リテラル、booleanリテラルまたは文字リテラルは、リテラルがソース・コード表現である値と評価されます。文字列リテラルまたはテキスト・ブロックは、3.10.5および3.10.6で指定されているクラスStringのインスタンスと評価されます。nullリテラルはnull参照と評価されます。
字句リテラルの評価は、常に正常に完了します。
15.8.6 テンプレート式
テンプレート式は、リテラル・テキストを式の値と組み合せる一般的な方法を提供します。テキストと式は、templateで指定されます。テキストと式の値を組み合せるタスクは、テンプレート・プロセッサに委任されます。
Stringへのテキストと値の単純な補間は、事前定義済のテンプレート・プロセッサSTR(7.3)から使用できます。他のテンプレート・プロセッサでは、テキストと値を任意の方法で組み合せて、Stringよりも高度な型の結果を生成できます。
- TemplateExpression:
-
TemplateProcessor
.TemplateArgument - TemplateProcessor:
- Expression
- TemplateArgument:
- Template
- StringLiteral
- TextBlock
- Template:
- StringTemplate
- TextBlockTemplate
- StringTemplate:
-
StringTemplateBegin EmbeddedExpression
{ StringTemplateMid EmbeddedExpression } StringTemplateEnd - TextBlockTemplate:
-
TextBlockTemplateBegin EmbeddedExpression
{ TextBlockTemplateMid EmbeddedExpression } TextBlockTemplateEnd - EmbeddedExpression:
- [ Expression ]
便宜上、ここでは3.13の次のプロダクションを示します。
- StringTemplateBegin:
"StringFragment\{- StringTemplateMid:
}StringFragment\{- StringTemplateEnd:
}StringFragment"- StringFragment:
- { StringCharacter }
- TextBlockTemplateBegin:
"""TextBlockFragment\{- TextBlockTemplateMid:
}TextBlockFragment\{- TextBlockTemplateEnd:
}TextBlockFragment"""- TextBlockFragment:
- { TextBlockCharacter }
テンプレートは、文字列テンプレートまたはテキスト・ブロック・テンプレートのいずれかです。文字列テンプレート(それぞれがテキスト・ブロック・テンプレート)は、文字列リテラル(テキスト・ブロック)に似ていますが、1つ以上の埋込み式が含まれます。埋込み式とは、先頭に文字シーケンス\{、文末に文字}が付加された式です。文字シーケンス\{と}の間に何も表示されない場合、埋込み式は暗黙的にnullリテラル(3.10.8)とみなされます。
n個の埋込み式(n>0)を含む文字列テンプレートは、n個の埋込み式を含むn+1個のフラグメントの代替インターリーブで構成されます。最初のフラグメントはStringTemplateBeginトークン(3.13)、次のn-1フラグメントはStringTemplateMidトークンであり、最後のフラグメントはStringTemplateEndトークンです。
いくつかのサンプル文字列テンプレートについて概要を次に示します。
文字列テンプレート
"\{42} is the answer."は、StringTemplateBeginトークン("\{)の後に式42(整数リテラル)、その後にStringTemplateEndトークン(} is the answer.")が続いています。文字列テンプレート
"The answer is \{x+y}!"は、StringTemplateBeginトークン("The answer is \{)、式x+y、StringTemplateEndトークン(}!")で構成されます。文字列テンプレート
"Hello \{name} from \{address.city},"は、StringTemplateBeginトークン("Hello \{)、式name、StringTemplateMidトークン(} from \{)、式address.city、StringTemplateEndトークン(},")で構成されます。最後に、文字列テンプレート
"Customer name: \{}"は、StringTemplateBeginトークン("Customer name: \{)、(暗黙的な)式null、StringTemplateEndトークン(}")で構成されます。
n個の埋込み式(n>0)を含むテキスト・ブロック・テンプレートは、n個の埋込み式を含むn+1個のフラグメントの代替インターリーブで構成されます。最初のフラグメントはTextBlockTemplateBeginトークン(3.13)、次のn-1フラグメントはTextBlockTemplateMidトークンであり、最後のフラグメントはTextBlockTemplateEndトークンです。
テンプレートのフラグメント文字列は、埋込み式を囲むリテラル・テキストを表します。フラグメント文字列は次のように決定されます。
n個の埋込み式とn+1個のフラグメントを含む文字列テンプレートには、n+1個のフラグメント文字列があります。各フラグメント文字列si (1≤i≤n+1)は、対応するフラグメント(3.13)の内容であり、すべてのエスケープ・シーケンスは、その内容に対して
String.translateEscapesを実行したかのように解釈されます。n個の埋込み式とn+1個のフラグメントを含むテキスト・ブロック・テンプレートにはn+1個のフラグメント文字列があり、次のように決定されます。
テキスト・ブロック・テンプレートの文字列の内容は、次のステップで指定される文字のシーケンスです。
TextBlockTemplateBeginの内容の後に文字シーケンス
\{が続きます。TextBlockTemplateMidごとに、文字
}で始まり、その後にTextBlockTemplateMidの内容、次に文字シーケンス\{が続く文字のシーケンスです。文字
}で始まり、その後にTextBlockTemplateEndの内容が続く文字のシーケンスです。
文字列の内容は、次のステップを順番に適用することでさらに変換されます。
文字列の内容の文字に対して
String.stripIndentを実行した場合と同じく、付随する空白はすべて削除されます。すべてのエスケープ・シーケンスは、ステップ1の結果である文字に対する
String.translateEscapesの実行によるものとして解釈されます。
フラグメント文字列s1、...、sn+1は、次のように文字列の内容から導出されます。
s1は、その内容がステップ2で生成された文字列の先頭から始まり、文字シーケンス
\{}の初回の直前に終了する文字のシーケンスである文字列です。si (2 ≤ i ≤ n)は、その内容が、ステップ2から得られた文字列の内容で(i-1)番目の文字シーケンス
\{}の直後に始まり、i番目の文字シーケンス\{}の直前に終わる文字のシーケンスである文字列です。sn+1は、その内容が、ステップ2から得られた文字列の内容でn番目の文字シーケンス
\{}の直後に始まり、文字列の内容の終了直前に終わる文字のシーケンスである文字列です。
一部のサンプル・テンプレートのフラグメント文字列を次に示します。
文字列テンプレート
"\{42} is the answer."の2つのフラグメント文字列は、空の文字列の後に" is the answer."が続きます。文字列テンプレート
"The answer is \{x+y}!"の2つのフラグメント文字列は、"The answer is "の後に"!"が続きます。文字列テンプレート
"Hello \{name} from \{address.city},"の3つのフラグメント文字列は、"Hello "、" from "、","の順に続きます。文字列テンプレート
"Customer name: \{}"の2つのフラグメント文字列は、文字列"Customer name: "の後に空の文字列が続きます。テキスト・ブロック・テンプレートの2つのフラグメント文字列
""" Name: \{customerName}"""最初はその内容が6文字のシーケンス
Name:LF(付随する空白は削除されていることに注意してください)の文字列、その後に空の文字列が続きます。
TPはTemplateProcessor式の型であるとします。TPはStringTemplate.Processorのサブタイプである必要があります。そうでないと、コンパイル時にエラーが発生します。
StringTemplate.Processor<R,E>は汎用関数インタフェース(9.8)であり、単一のabstractメソッドprocessには、単一のStringTemplate仮パラメータ、戻り型R、および型Eのthrows句があります。
Rは、型TPの、メソッドprocess(StringTemplate)の戻り型であるとします。テンプレート式の型は、取得変換(5.1.10)の後の型Rです。
テンプレートに表示される埋込み式の型に制限はありません。
例15.8.6-1.単純なテンプレート
次の簡単な例では、すべてのコンパイル・ユニットに暗黙的にインポートされ、単純な文字列補間を実装するStringTemplateのstaticメンバーSTRを使用します。
// A string template with simple embedded string variables
String firstName = "Joan";
String lastName = "Smith";
String fullName = STR."\{firstName} \{lastName}";
// A string template with embedded integer expressions
int x = 10, y = 20;
String s1 = STR."\{x} + \{y} = \{x + y}";
// Embedded expressions can invoke methods and access fields
String s2 = STR."You have a \{getOfferType()} waiting for you!";
String s3 = STR."Access at \{req.date} \{req.time} from \{req.ipAddress}";
// A text block template modeling an HTML element with
// embedded expressions
String title = "My Web Page";
String text = "Hello, world";
String html = STR."""
<html>
<head>
<title>\{title}</title>
</head>
<body>
<p>\{text}</p>
</body>
</html>
""";
// A text block template modeling a JSON value with
// embedded expressions
String name = "Joan Smith";
String phone = "555-123-4567";
String address = "1 Maple Drive, Anytown";
String json = STR."""
{
"name": "\{name}",
"phone": "\{phone}",
"address": "\{address}"
}
""";
実行時に、テンプレート式は次のように評価されます。
TemplateProcessor式が評価されます。結果の値がnullの場合、
NullPointerExceptionがスローされ、その理由からテンプレート式全体が突然完了します。TemplateProcessorの評価が突然完了する場合、テンプレート式全体が同じ理由で突然完了します。TemplateArgumentがStringLiteralまたはTextBlockの場合、このステップの結果は
StringTemplateのインスタンスで、引数TemplateArgumentを使用してstaticメソッドStringTemplate.ofを呼び出したものとして生成されます。TemplateArgumentがTemplateの場合、埋込み式e1, ..., en (n > 0)が評価され、embedded values, v1, ..., vnが生成されます。埋込み式は、テンプレートに表示される左から右の順序で評価されます。埋込み式の評価が突然完了する場合、テンプレート式全体が同じ理由で突然完了します。
それ以外の場合、このステップの結果は、次のプロパティを持つクラスのインスタンスへの参照になります。
StringTemplateインタフェースを実装したクラスであること。埋込み値v1, ..., vnを含む
java.util.Listのインスタンスをこの順序で返すインスタンス・メソッドvalues。テンプレートのフラグメント文字列だけを含む
java.util.Listのインスタンスを順に返すインスタンス・メソッドfragments。(1)テンプレートの正確なフラグメント文字列、および(2)埋込み値v1, ..., vnを最初のフラグメント文字列から順番に連結した厳密な代替インターリーブ文字列を返すクラス・インスタンスのインスタンス・メソッド
interpolate。
テンプレート式を評価した結果は、ステップ1の結果でメソッド
processを呼び出したものとして、ステップ2の結果から指定された引数を使用して決定されます。このメソッド呼出しが突然完了する場合、テンプレート式全体が同じ理由で突然完了します。
つまり、テンプレート式の意味は次のとおりです。
e."..." // "..." is a valid string templateは、メソッド呼出し式の意味に相当します。
e.process(t)ここで
tは、テンプレート内で埋込み式のリテラル・テキストと値の両方をカプセル化するStringTemplateのインスタンスを表します。
例15.8.6-2.単純なテンプレート・プロセッサ。
StringTemplateのinterpolateメソッドは、テンプレートのフラグメント文字列と埋込み値を連結して文字列を生成する便利な方法を提供します。次の例では、UPPERは指定されたテンプレートを補間しながら、すべての文字を大文字に変換します。
StringTemplate.Processor<String, RuntimeException> UPPER = (StringTemplate st) ->
st.interpolate().toUpperCase();
String name = "Joan";
String result = UPPER."My name is \{name}";
これらの文の実行後、resultは文字列"MY NAME IS JOAN"で初期化されます。
例15.8.6-3.より複雑なテンプレート・プロセッサ。
より複雑なテンプレート・プロセッサでは、次の単純なプログラミング・パターンを使用できます。次の例では、MY_UPPER_STRINGSが(valuesメソッドから返された埋込み値を使用する)interpolateメソッドによって文字列結果を返す前に、(fragmentsメソッドによって返された)フラグメント文字列を大文字に変換します。
StringTemplate.Processor<String, RuntimeException> MY_UPPER_STRINGS = (StringTemplate st) -> {
List<String> fragments = st.fragments()
.stream()
.map(String::toUpperCase)
.toList();
List<Object> values = st.values();
return StringTemplate.interpolate(fragments, values);
};
String name = "Joan";
String result = MY_UPPER_STRINGS."My name is \{name}";
これらの文の実行後、resultは文字列"MY NAME IS Joan"で初期化されます。
次の例では、MY_UPPER_VALUESは、補間の前に(null値をすべて処理しながら)埋込み式を大文字の文字列に変換します。
StringTemplate.Processor<String, RuntimeException> MY_UPPER_VALUES = (StringTemplate st) -> {
List<String> values = st.values()
.stream()
.map((o) -> (o==null)?"":o.toString().toUpperCase())
.toList();
return StringTemplate.interpolate(st.fragments(), values);
};
String title = null;
String firstName = "Joan";
String familyName = "Smith";
String result = MY_UPPER_VALUES."Welcome \{title}\{firstName} \{familyName}";
これらの文の実行後、resultは文字列"Welcome JOAN SMITH"で初期化されます。
例15.8.6-4.文字列を返さないテンプレート・プロセッサ
テンプレートを処理し、文字列以外の値を返すことができます。次の例では、JSONは、文字列ではなくクラスJSONObjectのインスタンスを返します。
StringTemplate.Processor<JSONObject, RuntimeException> JSON =
(StringTemplate st) -> new JSONObject(st.interpolate());
String name = "Joan Smith";
String phone = "555-123-4567";
String address = "1 Maple Drive, Anytown";
JSONObject doc = JSON."""
{
"name": "\{name}",
"phone": "\{phone}",
"address": "\{address}"
}
""";
例15.8.6-6.例外をスローできるテンプレート・プロセッサ
テンプレートが要件を満たさない場合、特定のテンプレートを検証し、例外をスローすることが役立つ場合があります。
次の例では、JSON_VALIDATEが指定のテンプレートをクラスJSONObjectのインスタンスに変換しますが、まず中間補間文字列が(先頭または末尾の空白を無視し)、一致する中カッコ内で始まり、終了しているかをチェックします。これらのチェックのいずれかが失敗した場合はJSONExceptionがスローされます。それ以外の場合は対応するJSONObjectインスタンスが返されます。
StringTemplate.Processor<JSONObject, JSONException> JSON_VALIDATE = (StringTemplate st) -> {
String stripped = st.interpolate().strip();
if (!stripped.startsWith("{") || !stripped.endsWith("}")) {
throws new JSONException("Missing brace");
}
return new JSONObject(stripped);
};
String name = "Joan Smith";
String phone = "555-123-4567";
String address = "1 Maple Drive, Anytown";
try {
JSONObject doc = JSON_VALIDATE."""
{
"name": "\{name}",
"phone": "\{phone}",
"address": "\{address}"
}
""";
} catch (JSONException ex) {
...
}