このドキュメントでは、Java SE 21のプレビュー機能である文字列テンプレートをサポートするためのJava言語仕様の変更について説明します。この機能の概要は、JEP 430を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2023-05-26: 編集上のマイナーな変更。
2023-03-29: SimpleProcessor
およびStringProcessor
インタフェースの削除。
2023-03-23: 新しいパッケージ組織を反映した名前の変更。
2023-03-01: その他のマイナーな変更。
2023-02-22: フィードバックに従ったマイナーな変更。
2023-02-09: 第3稿。様々な編集変更に加え、その他の重要な変更には次のものがあります。
- 3.13で完全に指定されたテンプレートのトークン化(導入された新しい曖昧さとその解決方法を含む)
- テキスト・ブロック・テンプレートの処理を改善。
- 全体的に多数の例を含む。
2022-11-15: 第2稿。字句と構文の文法に関する主な変更。テンプレート用に新しい用語を導入しました。
2022-01-20: 初稿リリース。
第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:
-
import
static
TypeName.
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:
-
import
static
TypeName.
*
;
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
が出力されます。
第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個のフラグメント文字列があり、各フラグメント文字列は対応するフラグメント(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."
のフラグメント文字列は、最初に空の文字列、次に文字列" is the answer."
が続きます。文字列テンプレート"The answer is \{x+y}!"
のフラグメント文字列は、文字列"The answer is "
の後に"!"
が続きます。文字列テンプレート"Hello \{name} from \{address.city},"
のフラグメント文字列は、文字列"Hello "
、" from "
、","
と続きます。最後に、文字列テンプレート"Customer name: \{}"
のフラグメント文字列は、最初に文字列"Customer name: "
の後に空の文字列が続きます。テキスト・ブロック・テンプレートのフラグメント文字列
""" Name: \{customerName}"""
最初はその内容が6文字のシーケンス
N
a
m
e
:
LF
(付随する空白は削除されていることに注意してください)の文字列、その後に空の文字列が続きます。
TemplateProcessor式の型TPはStringTemplate.Processor
のサブタイプである必要があり、そうでない場合はコンパイル時エラーが発生します。TPがStringTemplate.Processor<Result,Exc>
のサブタイプで、Result
およびExc
がパラメータ化型の型引数の場合、テンプレート式の型はResult
です。TPがRAW型StringTemplate.Processor
のサブタイプである場合、テンプレート式の型はObject
です。
StringTemplate.Processor<R,E>
は汎用関数型インタフェース(9.8)で、単一のabstract
メソッドprocess
には、StringTemplate
仮パラメータ、戻り型R
、型E
のthrows
句があります。
テンプレートに表示される埋込み式の型に制限はありません。
例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) {
...
}