Do you find Java multiline Strings not readable?
Multi line strings in java are often not readable which makes it error-prone as well. JDK 15 Standard feature Text blocks provide us a better way to write strings that span several lines of source code.
Let’s check the java code example to understand how text blocks make our code more readable and bug-free.
Can you spot a bug in the multiline String below?
String oldMultiLineStringSQL = "select emp_id, emp_name, emp_num_of_kids, emp_active" +
"from employee_table" +
"where employee_num_of_kids =1";
System.out.println(oldMultiLineStringSQL);
Following is the console output for oldMultiLineStringSQL.
select emp_id, emp_name, emp_num_of_kids, emp_activefrom employee_tablewhere employee_num_of_kids =1
In this output notice the words underlined in blue, here we don’t have spaces between words of consecutive lines. It causes SQLSyntaxErrorException. Next section we will see how this problem can be solved by Text blocks.
Text Blocks Java code
Now the same code we are writing using Java Text Blocks.
String sqlWithTextBlocks = """
select emp_id, emp_name, emp_num_of_kids, emp_active
from employee_table
where employee_num_of_kids =1
""";
Following is console output for text blocks, it is the same as we have written in code. No issues due to space among consecutive lines.
select emp_id, emp_name, emp_num_of_kids, emp_active from employee_table where employee_num_of_kids =1
The deep dive into Text block syntax
A text block consists of zero or more content characters, enclosed by opening and closing delimiters.
- The opening delimiter is a sequence of three double-quote characters (""") followed by zero or more white spaces followed by a line terminator.
- The closing delimiter is a sequence of three double-quote characters.
- The content begins at the first character after the line terminator of the opening delimiter and ends before the closing delimiter.
Text blocks compile-time processing
The content of a text block is processed by the Java compiler in three steps in the same sequence as given below:
-
Line terminators: Line terminators in the content are translated to LF (\u000A).
-
Common white spaces removal: Incidental white space surrounding the content, introduced to match the indentation of Java source code is removed in this step.
-
Escape sequence processing: Escape sequences in the content are interpreted in this step. Performing interpretation as the final step means developers can write escape sequences such as \n without them being modified or deleted by earlier steps.
The following sections have all 3 steps of compile-time processing in more detail.
Line terminators
Different operating systems have their Line terminators. All line terminators (CR/LF/CRLF) in the content are translated into LF (\u000A). It makes the same java code work across all platforms.
White space removal
Following two rules help us understand whitespace removal.
- There has to be one line terminator immediately after the initial opening delimiter.
- Now we have content and closing delimiter. if we move any line of content or closing delimiter to left it reduces common whitespace prefix. In other words, left most character in content or end delimiter decides the starting character of all lines in the text block.
Let’s check some examples to understand how it works in practice.
In all the examples
- dots (.) are used to show spaces in code.
- Vertical bars (|) are used to visualize the left margin.
This is the first example having no whitespaces in the output.
public static void printTextBlock() {
....String textBlock = """
............First line of test block
............Second line of test block
............""";
....System.out.println(textBlock);
}
Following is the Intellij idea screenshot for the above code.
Following is the output, showing all incidental white spaces removed.
|First line of test block
|Second line of test block
|
This is the second example showing initial character position in the text block is decided by the start of the second line in text block content,
out of all lines of content and end delimiter here, the second line has leftmost character.
public static void printInitialCharacterPositionDecidedByLeftMostCharacterOfLines() {
....String textBlock = """
............First line of test block
........Second line of test block
............""";
....System.out.println(textBlock);
}
Following is the Intellij idea screenshot for the above code.
Following is the output of the second example. We can see the initial 4 spaces that are common are removed in output.
| First line of test block
|Second line of test block
|
This is the third example showing the effect of moving the end delimiter to left.
public static void printInitialCharacterPositionDecidedByEndDelimiter() {
....String textBlock = """
............First line of test block........
............Second line of test block........
........""";
....System.out.println(textBlock);
}
Following is the Intellij idea screenshot for the above code.
In the following output of the third example. We can see spaces at the end of the line are removed. Moving the end delimiter 4 spaces to left adds 4 spaces in all the lines.
| First line of test block
| Second line of test block
|
This is the fourth example showing the effect of moving the end delimiter to right. Here we see moving the end delimiter to the right of content has no effect.
public static void printTextBlockMovingEndDelimiterToRightOfContentHasNoEffect()
{
....String textBlock = """
............First line of test block
............Second line of test block
................""";
....System.out.println(textBlock);
}
Following is the Intellij idea screenshot for the above code.
Following is the output of the fourth example, it shows no effect of moving the end delimiter to left, all common white spaces are removed.
|First line of test block
|Second line of test block
|
In all these examples you can see the green vertical bar in the IntelliJ idea shows the starting character of lines in the text block.
Escape processing
This is the third and last step of compile-time processing. We have seen first line terminators are interpreted, then the next step is white space removal and at the end escape processing. And because this is the last step in compile-time processing when we use \n (or any other escape sequence) in text block content, it will not be modified by the initial 2 steps and will be interpreted at the end.
Let’s learn different language features in escape processing using the below code example.
private static void printEscapeProcessing() {
....String textBlock = """
............"Hello\n
............Text Block " ' \\ \t "
............experiment another opening/closing delimiter type of 3 consecutive quotes \"""
............without newline concatenation of Strings \
............spaces \s\sat end in this way trailing spaces are not removed\s\s
............\"""";
....System.out.println("printEscapeProcessing");
....System.out.println(textBlock);
}
Following is the output of the above escape processing example.
|printEscapeProcessing
|"Hello
|
|Text Block " ' \ "
|experiment another opening/closing delimiter type of 3 consecutive quotes """
|without newline concatenation of Strings spaces ..at end in this way trailing spaces are not removed..
|"
In the above output few things to observe are:
- \n is allowed in the text block, and it generates an additional new line in the output.
- ” and ‘ can be used anywhere in text blocks like Strings.
- to use \ we need to use \\.
- Sequences of three “ characters require the escaping of at least one “ to avoid mimicking the closing delimiter.
- To allow finer control of the processing of the newlines and white space, two new escape sequences are introduced in java 15, these were not present in earlier preview versions.
- \ at the end acts like a concatenation of 2 Strings, in other words, it avoids line terminator between consecutive lines.
- \s adds space, in example output spaces are shown with dots (.)
Common mistakes in text blocks
Here are some examples of ill-formed text blocks:
String a = """"""; // no line terminator after opening delimiter
String b = """ """; // no line terminator after opening delimiter
String c = """
"; // no closing delimiter
String d = """
abc \ def
"""; // unescaped backslashs
At the end
Knowing language features like this helps you get the best java jobs, that’s why to help you I wrote the ebook 5 steps to Best Java Jobs. Download this step by step guide for free!
Resources
- https://openjdk.java.net/jeps/378, This is the Java enhancement proposal for text blocks in JDK15.
- https://github.com/Vipin-Sharma/JDK15Examples, this is the link to code examples used in this post.