JEP 430「文字列テンプレート(プレビュー)」は、Javaプログラミング言語の強化を提案する機能JEPタイプで、文字列リテラルに似ているが、実行時に文字列テンプレートに組み込まれる埋め込み式が含まれており、JDK 21の提案から目標に格上げされた。
Java開発者は、文字列リテラルとテキストブロックを文字列テンプレートで強化できるようになり、リテラルテキストを埋め込み式やプロセッサと結合させることで特殊な結果を生み出すことができる。この新機能の目的は、Javaプログラムの記述の簡素化、テキストと式が混在する式の可読性の向上、ユーザー提供の値から文字列を構成するJavaプログラムのセキュリティ強化である。
このJEPでは、開発者が文字列の補間を行い、安全かつ効率的に文字列を合成できるように、テンプレート式と呼ばれる新しい種類の式を導入する。テンプレート式はプログラム可能であり、文字列の合成に限定されない。テンプレート式はプログラム可能であり、文字列の合成に限らず、構造化されたテキストをドメイン固有のルールに従ってあらゆる種類のオブジェクトに変換できる。テンプレート式では、テンプレートプロセッサが、テンプレート内のリテラルテキストと、実行時に埋め込まれた式の値を組み合わせて、結果を作成する。例としては、以下のようなものがある。
String name = "Joan";
String info = STR. "My name is \{name}";
assert info.equals("My name is Joan"); // true
テンプレート式は、文字列リテラルと似た構文を持つが、接頭辞が付く。上のコードの2行目には、テンプレート式が含まれている。
これに対し、通常、文字列補間は、多くのプログラミング言語が採用しているように、プログラマーが文字列リテラルと式を1つの文字列にまとめることができ、従来の文字列連結よりも利便性と明確性を高めることができる。しかし、特にSQL文、HTML/XML文書、JSONスニペット、シェルスクリプト、自然言語テキストを扱う場合、他のシステムで誤って解釈される可能性のある危険な文字列を作成できる。セキュリティの脆弱性を防ぐため、Javaでは、エスケープやバリデイトメソッドを使って、式を埋め込んだ文字列を検証し、サニタイズすることが開発者に求められている。
より安全で効率的な解決策は、文字列を構成するためのファーストクラスのテンプレートベースのメカニズムを導入することで、テンプレート固有のルールを自動的に文字列に適用し、SQL文のエスケープされた引用符、HTML文書の不正ではないエンティティ、および定型文なしのメッセージローカリゼーションを実現することにある。このアプローチにより、開発者は、埋め込まれた各式をエスケープし、文字列全体を検証するという手作業から解放される。まさにこれが、他の一般的なプログラミング言語が使用する文字列補間とは対照的に、Javaが準拠するテンプレート式で行われていることである。
テンプレート式の設計により、式が埋め込まれた文字列リテラルやテキストブロックから、式の値が補間された文字列に直接移動することは不可能である。これは、危険なほど不正確な文字列がプログラム中に蔓延するのを防ぐためである。その代わりに、STR、FMT、RAWなどのテンプレート処理系が、文字列リテラルを処理し、その結果を検証し、埋め込まれた式の値を補間している。
以下は、HTMLテキスト、JSONテキスト、ゾーンフォームを記述するために複数の行を使用したテンプレート式の例である。
String title = "My Web Page";
String text = "Hello, world";
String html = STR."""
<html>
<head>
<title>\{title}</title>
</head>
<body>
<p>\{text}</p>
</body>
</html>
""";
これで、次のような出力が得られる。
| """
| <html>
| <head>
| <title>My Web Page</title>
| </head>
| <body>
| <p>Hello, world</p>
| </body>
| </html>
| """
別の例として、次のようなものがある。
String name = "Joan Smith";
String phone = "555-123-4567";
String address = "1 Maple Drive, Anytown";
String json = STR." ""
{
"名前": "\{name}",
"電話番号":"\{phone}",
"住所": "\{address}"
}
""";
同様に、これは次のような出力を生成する。
|"""
| {
| "名前": "Joan Smith",
| "電話番号":"555-123-4567",
| "住所": "1 Maple Drive, Anytown"
| }
| """
別の例を挙げる。
record Rectangle(String name, double width, double height) {
double area() {
return width * height;
}
}
Rectangle[] zone = new Rectangle[] {
new Rectangle("Alfa", 17.8, 31.4),
new Rectangle("Bravo", 9.6, 12.4),
new Rectangle("Charlie", 7.1, 11.23),
};
String form = FMT."""
Description Width Height Area
%-12s{zone[0].name} %7.2f{zone[0].width} %7.2f{zone[0].height} %7.2f{zone[0].area()}
%-12s{zone[1].name} %7.2f{zone[1].width} %7.2f{zone[1].height} %7.2f{zone[1].area()}
%-12s{zone[2].name} %7.2f{zone[2].width} %7.2f{zone[2].height} %7.2f{zone[2].area()}
\{" ".repeat(28)} Total %7.2f{zone[0].area() + zone[1].area() + zone[2].area()}
""";
上記のコードは、次のような出力を生成する。
| """
Description Width Height Area
Alfa 17.80 31.40 558.92
Bravo 9.60 12.40 119.04
Charlie 7.10 11.23 79.73
Total 757.69
"
Javaには、文字列補間を行うためのテンプレートプロセッサが2つ用意されている。STR
と FMT
である。STR
はテンプレート中の各埋め込み式をその(文字列化された)値で置き換え、FMT
は埋め込み式の左側に現れる書式指定子を解釈する。書式指定子はjava.util.Formatter
で定義されているものと同じである。未処理のテンプレートが必要な場合は、標準の RAW
テンプレートプロセッサを使用できる。このプロセッサは、補間や処理を一切行わず、単に元のテンプレートを返すだけである。
さらに、開発者が独自のテンプレートプロセッサを作成し、テンプレート式で使用することもできる。テンプレートプロセッサは、機能インターフェイスValidatingProcessorを提供するオブジェクトであり、そのクラスは、StringTemplateを受け取ってオブジェクトを返すValidatingProcessorの単一の抽象メソッドを実装する。テンプレートプロセッサは、実行時に検証を行うようにカスタマイズでき、文字列だけでなく、あらゆる型のオブジェクトを返すことができる。
結論として、Javaのテンプレート式は、開発者が文字列の補間や文字列の合成を簡単かつ安全に行えるようにするものである。