テキスト・ブロックとは
テキスト・ブロックは、改行やクォーテーションなどを含んだ文字列を簡単に定義できる新しい文法である。JavaのStringクラスを使って文字列を宣言する場合、通常であれば、改行や引用符は次のようにエスケープ文字を使用して記述する必要がある。また、文字列を複数行に分けて記述したい場合には連結演算子(+)を使わなければならない。次のような具合だ。
String str = "Hello, Mynavi readers,\n" +
"This is an article \n" +
"that introduces new features of the \"modern\" Java platform.\n";
この記述法は非常によく使うにもかかわらず、コードを読みづらくする上に記述量も増えるという問題を抱えていた。テキスト・ブロックを使用した場合、上のコードは次のように記述できる。
String str = """
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
""";
ダブルクォーテーションを3つつなげたもの(""")が、テキスト・ブロックの開始と終了を表す。テキスト・ブロックの中では、改行などの特殊文字についてもそのまま記述することができる。したがって、このStringをprintfで出力すると次のようになる。
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
ただし、開始の"""の後には文字列をつなげることはできず、必ず改行しなければならない("""と改行の間の空白は無視される)。
テキスト・ブロックはJEP 378として、仕様の策定と実装が進められている。最初のプレビュー版(JEP 355)が使えるようになったのはJava 13で、その後エスケープ文字の扱いなど若干の仕様変更が加わったものがJEP 368としてJava 14に追加された。予定通り進めば、2020年9月にリリースされるJava 15で正式版となる。
テキスト・ブロックの使い方
テキスト・ブロックは、現時点ではまだプレビュー版なので、普通にコンパイルすると次のようにエラーになってしまう。
$ javac TextBlockSample.java
TextBlockSample.java:3: エラー: テキスト・ブロックはプレビュー機能であり、デフォルトで無効になっています。
String str = """
^
(テキスト・ブロックを有効にするには--enable-previewを使用します)
エラー1個
プレビュー版の機能を使いたい場合は、次のように「--enable-preview」と「--release」の2つのコンパイルオプションを明示的に指定すればよい。今回は対象バージョンがJava 14なので、--releaseには14を指定している。警告が出るが、意図的に使っているのでこれは無視してもよい。
$ javac --enable-preview --release 14 TextBlockSample.java
注意:TextBlockSample.javaはプレビュー言語機能を使用します。
注意:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
実行時も、同様に --enable-preview オプションを指定する。
$ java --enable-preview TextBlockSample
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
ちなみにIntelliJ IDEAでは、次のようにLanguage Levelの指定によってプロジェクト単位でプレビュー機能を使用するか否かを選択できるようになっている。
テキスト・ブロックは、内部的には通常のStringと同じなので、次のようにStringリテラルが入れられる場所であればどこでも使うことができる。
System.out.println("""
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
""");
インデントは、ブロック中の文字列の一番インデントが浅い行が基準になる。次の例の場合、「Hello, Mynavi readers,」がゼロ・インデントで、ほかの行はこの位置を基準に先頭に空白が挿入される。
String str = """
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
""";
この文字列は、2行目と3行目がインデントされて次のようになる。
Hello, Mynavi readers,
This is an article
that introduces new features of the "modern" Java platform.
テキスト・ブロック内では、行末の空白は無視される。もし、明示的に空白を挿入したい場合は、次のように「\s」を入れればよい。
String str = """
Hello, Mynavi readers,\s
This is an article\s\s
that introduces new features of the "modern" Java platform.
""";
行末の「\s」の部分は、次のようにすべて空白に置き換わる(「_」の部分は実際は空白)。
Hello, Mynavi readers,_
This is an article__
that introduces new features of the "modern" Java platform.
改行をエスケープしたい場合は、行末に「\」を記述する。
String str = """
Hello, Mynavi readers, \
This is an article \
that introduces new features of the "modern" Java platform.
""";
この場合、次のように改行がない文字列になる。
Hello, Mynavi readers, This is an article that introduces new features of the "modern" Java platform.
テキスト・ブロックで定義した文字列は、通常の文字列と同様に文字列連結子(+)で連結することもできる。次の例では、通常の文字列リテラルと、String変数、そしてテキスト・ブロックを連結している。
String target = "マイナビ読者";
String str6 = "Hello, " + target + ",\n" +
"""
This is an article
that introduces new features of the "modern" Java platform.
""";
結果は次のようになる。
Hello, マイナビ読者,
This is an article
that introduces new features of the "modern" Java platform.
Stringの連結を行いたい理由の1つとしは、上のように途中で変数の値を挿入したいということが挙げられる。+で連結してもよいが、Stringクラスに新たに追加されたformatted()メソッドを使って次のようによりシンプルに書くこともできる。
String str = """
Hello, %s,
This is an article
that introduces new features of the "modern" Java platform.
""".formatted("マイナビ読者");
fotmatted()メソッドを使うと、C言語のprintfのような形式で、文字列の途中にフォーマット指定子を使って値を挿入することができる。内部的には、既存のformat()メソッドを使ったフォーマットと同等のものになっている。このメソッドはテキスト・ブロックとセットで使うことを想定して用意されたものなので、Java 14時点ではテキスト・ブロックと同様にまだプレビュー扱いになっている。
テキスト・ブロックの実践例
実際の開発で、テキスト・ブロックを利用できたらうれ嬉しいシーンをいくつか考えてみよう。次のコードは、HTMLスニペットをコード内に埋め込む際にテキスト・ブロックを使った例である。従来のエスケープ文字だらけの記述に比べれば格段に可読性が上がっている。
String html = """
<html>
<body>
<p>Hello, Mynavi readers!</p>
</body>
</html>
""";
SQLのクエリをテキスト・ブロックを使って記述してみると、次のようになる。エスケープ文字がそのまま記述できるのが非常にうれしい。formatted()メソッドを組み合わせれば、一層使い勝手は上がりそうだ。
try(Connection connection = DriverManager.getConnection("URL", "USER", "PASSWORD")) {
Statement statement = connection.createStatement();
String query = """
SELECT *, FROM "employees"
WHERE "role" = 'manager'
ORDER BY "id";
""";
ResultSet resultSet = statement.executeQuery(query);
} catch (SQLException e) {
e.printStackTrace();
}
次のコードは、Scripting APIを利用してJavaScriptコードをJavaプログラム内で実行する例である。スクリプトを直接テキスト・ブロック内に書き込むことができる。
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
try {
String script = """
function hello() {
print('"Hello, Mynavi readers!"');
}
hello();
""";
Object obj = engine.eval(script);
} catch (ScriptException e) {
e.printStackTrace();
}
このように、テキスト・ブロックはシンプルながらコーディング作業を大幅に軽減し、コードの可読性も上げてくれる。プレビュー機能という点に留意する必要はあるが、実用性が高いのでぜひ活用したい新機能だ。