テキスト・ブロックとは

テキスト・ブロックは、改行やクォーテーションなどを含んだ文字列を簡単に定義できる新しい文法である。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();
}

このように、テキスト・ブロックはシンプルながらコーディング作業を大幅に軽減し、コードの可読性も上げてくれる。プレビュー機能という点に留意する必要はあるが、実用性が高いのでぜひ活用したい新機能だ。