※本記事は、Mala Guptaによる”Text Blocks Come to Java“を翻訳したものです。
長く待ち望まれてきた複数行文字列がJava 13で実現
著者:Mala Gupta
2019年10月16日
Java 13のテキスト・ブロックを使うことで、複数行文字列リテラルを簡単に使えるようになります。文字列リテラル内の特殊文字をエスケープすることや、複数行にまたがる値に連結演算子を使うことは不要になります。さらに、文字列を書式設定する方法も制御できるようになります。テキスト・ブロックとは、複数行文字列を表すJavaの用語です。テキスト・ブロックにより、コードの可読性は大幅に向上します。
本記事では、テキスト・ブロックとは何か、テキスト・ブロックによって対処できる問題、そしてテキスト・ブロックの使い方について説明します。それでは始めます。
テキスト・ブロックとは
Stringデータタイプはおそらく、Java開発者がもっともよく使う型の1つです。任意の言語で数文字の文字列から複数行文字列まで、何でも格納できます。しかし、この柔軟性のため、一部のString値を読み取ることや変更することが難しくなる場合があります。例を挙げるなら、引用符が埋め込まれている文字列、エスケープ文字、複数行にわたる文字列などです。
それでは、Java 13の新しいプレビュー機能であるテキスト・ブロックが、どのように役立つかについて見ていきます。
テキスト・ブロックを使用して、複数行Stringリテラルを簡単に定義することができます。通常のStringリテラルを使って実現した場合に見にくさの原因となる、連結演算子やエスケープ・シーケンスの追加が不要です。さらに、String値をどのように書式設定するかも制御できます。例として、次のHTMLスニペットについて考えてみます。
String html = """
<HTML>
<BODY>
<H1>"Java 13 is here!"</H1>
</BODY>
</HTML>""";
ブロックの開始と終了を表す3つの二重引用符に注目してください。以前のJavaでは代わりにどのように記述していたかについて考えてみます。
String html1 =
"<HTML>\n\t<BODY>\n\t\t<H1>\"Java 13 is here!\"</H1> \n\t</BODY>\n</HTML>\n";
次の方が一般的かもしれません。
String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 13 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";
いずれも、テキスト・ブロックほどわかりやすくはありません。
構文
前述のように、テキスト・ブロックは、3つの二重引用符(""")を、開始および終了を表すデリミタとして使って定義します。開始デリミタの後には、0個以上の空白と行終端文字(改行)を置くことができます。テキスト・ブロックの値は、この行終端文字の後から始まります。終了デリミタの場合、このようなルールはありません。
したがって、次の例は無効なテキスト・ブロックとなります。開始デリミタの後に行終端文字が存在しないからです。
String multilineValue1 = """ """;
String multilineValue2 = """""";
プレビュー言語機能
テキスト・ブロックは、Java 13でプレビュー言語機能としてリリースされました。しかし、プレビュー言語機能は未完成または開発途中の機能ではありません。実際のところ、開発者に使ってもらう準備は整っているものの、細かい部分は今後のJavaリリースで変更される可能性もある機能という意味です。このような扱いになっていることには、理由があります。
開発者は、6か月周期の新しいリリース・サイクルにより、言語の新機能を使えるようになっています。しかし、Javaチームは、Javaに言語機能を恒久的に追加する前に、その機能に対する開発者の意見について評価します。フィードバック次第で、プレビュー機能が微調整されてからJava SEに追加されることも、完全に削除されることもあります。そこで、テキスト・ブロックについてフィードバックがある方は、JDKメーリング・リスト(メンバー登録が必要です)で共有してください。
プレビュー言語機能を使うためには、コンパイル時と実行時に明確に有効化する必要があります。これにより、意図せずにプレビュー機能を使ってしまうことがないようにしています。テキスト・ブロックを含むソース・ファイルをコンパイルするためには、--enable-previewオプションと-release 13オプションを使用します。コマンドラインを使ってソース・ファイルJava13.javaをコンパイルする例を次に示します。
javac --enable-preview --release 13 Java13.java
プレビュー機能は変更される可能性もあることを強調するため、先ほどのコマンドを実行した際に、図1のようなコンパイラ警告が表示されます。

図1:>プレビュー機能が使われているコードに対して表示されるコンパイラ警告
クラスJava13を実行するときも、--enable-previewオプションを使う必要があります。
java --enable-preview Java13
次は、テキスト・ブロックの実装について見てみます。
同じStringデータタイプ
従来のString値もテキスト・ブロックも、コンパイルされると同じ型、すなわちStringになります。バイトコードのクラス・ファイルでは、String値が従来のStringによるものか、テキスト・ブロックによるものかは区別されません。このことは、テキスト・ブロックの値が文字列プールに格納されていることを示しています。
次のコードをご覧ください。皆さんは、変数traditonalStringとtextBlockStringが同じStringインスタンスを指すと思うでしょうか。
String traditionalString = "Java";
String textBlockString = """
Java""";
System.out.println(traditionalString == textBlockString);
この2つは内容が同じであるため、同じインスタンスを指します。先ほどのコードでは、trueが出力されます。
本記事の冒頭で、従来のStringでは複数行のString値が扱いづらくなることについて説明しました。続くいくつかのセクションでは、テキスト・ブロックがどのように役立つかについて取り上げます。
複数行の値の扱いを改善
開発者は、JSON、HTML、XMLや正規表現(regex)データなどの複数行文字列値を頻繁に扱います。テキスト・ブロックを使うことで、複数行JSON値の扱いが次のようにシンプルになります。
String json = """
{
"name": "web",
"version": "1.0.0",
"dependencies": "AppA"
}
""";
エスケープ・シーケンスや連結演算子によって見にくくなることがないため、JSON値を容易に編集できます。念のため、メリットを感じないと思う方のために、従来のStringで同じJSON値を定義した場合の例も挙げておきます。
String json =
"{" +
"\"name\": \"web\"," +
"\"version\": \"1.0.0\"," +
"\"dependencies\": \"AppA\" +
"}";
この例は、読者のSven Bloesl氏の提案によって改善されています。
SQL問合せをString値として格納するためには、SQL問合せをコピーして貼り付けるか、または自分で書くかのいずれかを行います。String変数を使って(Java 12またはそれ以前のバージョンで)複数行SQL問合せを次のようにして格納したとします。
String query =
"SELECT name, age" +
"FROM EMP" +
"WHERE name = \'John\'" +
"AND age > 20";
このコードは、無効な問合せを表しています。各行の末尾に空白がないため、この問合せは次のように解釈されます。
SELECT name, ageFROM EMPWHERE name = 'John'AND age > 20
Karim Ourrai氏とBrian Goetz氏の報告により、誤った例がこのセクションから削除されました。
テキスト・ブロックを使うことにより、同じような問題を避けることができます。
String query = """
SELECT name, age
FROM EMP
WHERE name = 'John'
AND age > 20
""";
テキスト・ブロック内のエスケープ・シーケンス
Stringリテラルと同様に、テキスト・ブロックにもさまざまなエスケープ・シーケンスを追加できます。たとえば、テキスト・ブロックに新しい行を含める場合、値を複数行にわたって配置することも、\nのようなエスケープ・シーケンスを使うこともできます。次のコードでは、I'mとhappyは別々の行になります。
String html = """
<HTML>
<BODY>
<H1>I'm \nhappy</H1>
</BODY>
</HTML>""";
ご想像のとおり、無効なエスケープ・シーケンスやエスケープしていないバックスラッシュは許可されません。
意味のない空白とインデント
大きな疑問として、不要な空白がどのように扱われるかが挙げられます。実は、この点はコンパイラがエレガントに処理してくれます。テキスト・ブロックでは、すべての行で一番左側にある非空白文字、または一番左側にある終了デリミタによって、意味のある空白が始まる場所が定義されます。
図2の場合、getHTML()で返されるString値の中で、一番左側にある非空白文字は<(<HTML>の最初の部分)です。この位置は、終了デリミタの位置とも一致しています。

図2:>不要な空白(青)と意味のある空白(緑)を表したコード
このコードでは、図3に示す文字列が返されます(最初と最後の行では、先頭に空白が含まれません)。

図3:>先ほどのコードから返される文字列(緑色の四角は文字列に含まれる空白を示します)
空白が削除されないように、必須としてマークするためには、終了デリミタ、またはいずれかの非空白文字を左に動かします。図4に示すように、終了デリミタ"""を8文字分左に動かしてみます。

図4:>終了デリミタを左に動かした、図2のコード
変更したコードでは、図5に示すString値が返されます。各行の先頭に8文字分の空白(緑色の四角)が追加されています。その他の空白も緑色の四角で表されています。

図5:>図4のコードから出力される文字列(先頭に空白(緑色の四角)が追加されています)
デフォルトでは、それぞれの行の末尾にある空白はテキスト・ブロックから削除されます。その空白を保持する必要がある場合は、8進エスケープ・シーケンス\040(ASCIIでは、空白は文字32となります)を使って強制的に空白を含めることができます。次に例を示します。この例では、テキスト・ブロックの2行目の末尾に空白を追加しています。
String campaign = """
Don't leave home without -
money &\040
carry bag.
Reduce | Reuse
""";
なお、必須の空白にタブ(\t)が含まれている場合、タブは展開されず、1つの空白としてカウントされることに注意してください。
テキスト・ブロックの連結
テキスト・ブロックは、従来のString値と連結できます。その逆も可能です。次に例を示します。
String concatenate() {
return """
Items to avoid -
Single
Use
Plastics
"""
+
"Let's pledge to find alternatives";
}
String値を連結する理由の1つに、変数の値の挿入があります。
String concatenate(Object obj) {
return """
Items to avoid -
Single
Use
"""
+ obj + """
Let's pledge to find
alternatives""";
}
テキスト・ブロックは、文字列が想定される任意の場所で使うことができます。そのため、たとえばString.replaceメソッドでも、特別な処理をせずに使うことができます。
String concatenateReplace(Object obj) {
return """
Items to avoid -
Single
Use
$type
Let's pledge to find
alternatives""".replace("$type", obj.toString());
}
同じように、format()をはじめとする、Stringの任意のメソッドも使用できます。
まとめ
テキスト・ブロックにより、開発者は複数行文字列値をより簡単に扱えるようになります。現時点でテキスト・ブロックはプレビュー機能であり、変更される可能性もあることに留意してください。 ただし、そのような状況ではあっても、コーディング作業を大いに軽減してくれるはずです。
Java Magazine 日本版Vol.47の他の記事
Java 13のswitch式と再実装されたSocket APIの内側
言語の内側:シールド型
TeaVMを使ってブラウザでJavaを動かす
ツールをよく知る
クイズに挑戦:1次元配列(中級者向け)
クイズに挑戦:カスタム例外(上級者向け)
クイズに挑戦:ロケールの読取りと設定(上級者向け)
クイズに挑戦:関数型インタフェース(上級者向け)
![]() |
Mala GuptaMala Gupta(@eMalaGupta):Java Champion。JetBrainsのデベロッパー・アドボケート。eJavaGuru.comの創設者で、認定試験に関する何冊かの人気書籍を執筆。Delhi Java User Groupの共同リーダーであり、Women Who Codeデリー支部のディレクターも務める。 |

