Introduction
Java 15 introduced Text Blocks. The main reason behind Text Blocks is that programmers can write multiline strings without having to specify escapes for the most common scenarios.
Goals
At the end of this tutorial, you would have learned:
- How to store multiline String using Text Block.
- Some edge cases to watch out for when using Text Blocks.
Prerequisite Knowledge
- Basic Java
Tools Required
- A Java IDE with support for JDK 15+.
Syntax
A Text Block can be used anywhere a String literal can be used. Instead of one pair of double quotes used in String literals, a Text Block uses 3 pairs of double quotes.
Here is a traditional multiline String of an HTML document:
var html = "<!DOCTYPE html>\n" +
"<html>\n" +
" <head>\n" +
" </head>\n" +
" <body>\n" +
" <p>Hello World</p>\n" +
" </body>\n" +
"</html>";
And here is a Text Block:
var html = """
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hello World</p>
</body>
</html>
""";
As you can see, the version using the Text Block no longer has to add the new line character (\n
) and concatenate every new line. Removing the need for String concatenation also removes all of the extra double quotes (“
) and plus signs (+
), improving readability.
Edge Cases
Text Block has compiler support, so syntax errors are mostly obvious. But there are quite a few edge cases that programmers should watch out for when working with Text Blocks.
The Final New Line
If the closing triple quotes are on a new line of their own, Java will insert an extra new line character (\n
) at the end.
This method will return a String with an extra new line at the end.
private static String withNewline(){
return """
Hello
World
""";
}
While this method will return a String without an extra new line.
private static String noNewline(){
return """
Hello
World""";
}
Both are valid code that will compile, but the behavior will not be flagged by IDEs and can certainly catch programmers off guard.
Leading White Space
IDEs tend to insert extra spaces and tabs when formatting our code, but how does javac
(the compiler) determine whether some white space is part of the String?
Take a look at the withNewline()
method below. There are at least 16 spaces from the beginning of the line to the word “Hello”.
To simplify the concept, we only need to remember that the starting position of the closing triple quotes is the cutoff point. Everything before that will be discarded by the compiler.
private static String zeroSpaces(){
return """
Hello
World
""";
}
private static String eightSpaces(){
return """
Hello
World
""";
}
private static String sixteenSpaces(){
return """
Hello
World
""";
}
The 3 methods above exemplify the different positions of the closing triple quotes. They print:
Hello
World
Hello
World
Hello
World
When a final new line is purposely skipped by the programmer, you must use the String#indent
method to set the indentation of the Text Block.
private static String fourSpaces(){
return """
Hello
World""".indent(4);
}
Output:
Hello
World
Trailing White Space
Just like leading white space, trailing white space is also stripped out by the compiler. There are 3 official strategies that you can adapt to preserve trailing white spaces: character substitution, character fence, and octal escape sequence for space. You can find the code for these methods below.
private static String charSub() {
return """
Hello$$$$
World
""".replace('$', ' ');
}
private static String charFence() {
return """
Hello |
World|
""".replace("|\n", "\n");
}
private static String octalEscape() {
return """
Hello\040\040\040
World
""";
}
It is hard to tell if the output actually preserved the trailing white spaces, but it is possible if you place your cursor at the end of the output in your IDE terminal. In IntelliJ, I can highlight the extra white spaces on the lines that were preserved like the screenshot below.
Solution Code
package com.example;
public class Entry {
public static void main(String[] args){
}
private static String noBlock(){
var html = "<!DOCTYPE html>\n" +
"<html>\n" +
" <head>\n" +
" </head>\n" +
" <body>\n" +
" <p>Hello World</p>\n" +
" </body>\n" +
"</html>";
return html;
}
private static String textBlock(){
var html = """
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hello World</p>
</body>
</html>
""";
return html;
}
private static String withNewline(){
return """
Hello
World
""";
}
private static String noNewline(){
return """
Hello
World""";
}
private static String zeroSpaces(){
return """
Hello
World
""";
}
private static String eightSpaces(){
return """
Hello
World
""";
}
private static String sixteenSpaces(){
return """
Hello
World
""";
}
private static String fourSpaces(){
return """
Hello
World""".indent(4);
}
private static String charSub() {
return """
Hello$$$$
World
""".replace('$', ' ');
}
private static String charFence() {
return """
Hello |
World|
""".replace("|\n", "\n");
}
private static String octalEscape() {
return """
Hello\040\040\040
World
""";
}
}
Summary
Because we have given up precision for convenience, it is important to keep these edge cases in mind when working with Text Blocks, especially when storing white space-sensitive documents. For example, XML-based documents are more forgiving than something like Python, Yaml, when white spaces are used incorrectly.
You can find all of the code used in this article here https://github.com/dmitrilc/DaniWebJavaTextBlock/tree/master