This Articles Contents
Introducing Text Blocks
Hope you gone thought the Java 15 Introduction
Let’s dive into the text blocks language feature. Text blocks started out as a preview feature in the Java language, but it now has graduated to be part of the Java language specification. Since it’s now an official part of the language, you can start using it in production code, and the feature is here to stay. Now let’s explore why text blocks were added and what they can do. Text blocks actually address a long‑standing issue in the Java language. Up until now, there was no real way to have multiline string literals in source code.
Whenever you wanted to have a string literal in source code that spans multiple lines, you had several options, but none of them were great. Let’s say we want to create a string containing JSON in a pretty printed format using new lines. One way would be to create a single string literal. First of all, this leads to pretty long lines, which isn’t really great in your source code, but more importantly, the multiline nature of the string is obscured. You have to use escape sequences to indicate new lines in the string.
And to top it off, you have to escape the quotes that are part of the JSON using a backslash. All of this noise in the string makes it very hard to see what is actually going on. Usually we try to alleviate this by splitting the string literal into multiple string literals and then concatenating them using plus. This is somewhat better, but you still have the explicit new line escape sequences and the escapes for the double quotes, so unfortunately, it still doesn’t really look like JSON.
Some people prefer to use the string builder in this case to do the concatenation, but the point still stands. We can only express multiple lines of text using multiple string literals and concatenating them. And the visual noise of the escape sequences makes it pretty hard to see the real intention of these multiple lines of text. The text blocks feature solves all of these issues. It allows you to freely express multiple lines of text using the new syntax shown here. A text block always starts with three double quotes and a new line.
Then you can enter as many lines of text as you want in the source code until you close the text block again using three double quotes. In fact, the text block you see here leads to the exact same string that we saw in the previous examples. The new lines that are introduced here inside of the text block are preserved into the resulting string. Also note that we don’t have to escape the quotes around the keys and the values in the JSON anymore. Inside a text block, a double quote can be freely used. In the end, a text block will result in a regular string.
At runtime there’s absolutely no difference between this string being created as a text block or using regular strings in concatenation, as we saw before. However, I hope you’ll agree with me that this looks much, much nicer. Just by looking at this text block, it’s fully clear that we’re dealing with JSON, and there’s no syntactic noise to obscure this fact. You might be wondering about whitespace handling and indentation for this text block, and this is something that we’ll dive into a bit later.
Now you know the reason why text blocks were introduced into Java, Text blocks started out as a preview feature, first in Java 13 and the second iteration in Java 14, and after feedback and improving the feature, it’s now officially part of the Java 15 language. Wherever you currently have multiline strings in your source code, either using long lines or using concatenation, you can start using text blocks. And remember, text blocks don’t introduce a new type; they will result in a regular string. In fact, at runtime, you cannot even detect the difference between a string that came from a text block or a string that came from a regular string literal. Now that you’ve gotten a taste of this feature, it’s time to dive into more details around how text blocks work.
Working with Text Blocks
Here’s the example text block you saw earlier containing some JSON. Since text blocks ultimately are regular strings, you can use them anywhere in source code where you can also use regular string literals. Here we assign a text block to a variable, but you can just as well pass a text block as a parameter, for example, to methods like println. As long as you use the correct syntax with three double quotes followed by a newline and then again closing the text block using three double quotes, you’re good to go. The text block shown here translates into the following string.
As you can see, there are newline characters in there that correspond to the newlines that we have in our text block, and some of the indentation is preserved, but not all of it. That raises the question, how is a text block actually translated into a string? There are three important processing steps that the compiler applies to text blocks. First of all, it will normalize the line endings. What does this mean?
Well, you can have Java source code stored on Windows or Linux, and either of these operating systems have different representations for line endings. It will be a bad thing if source code containing text blocks compiled on Windows would yield a different string than the same source code compiled on Linux. In order to avoid these differences across platforms, all newlines inside of a text block are normalized into UNIX line endings, meaning a single line feed character will be used equivalent to the escape we use in Java strings. This normalization avoids nasty surprises when working with text blocks across different platforms. The second processing step is about whitespace handling.
We’ll go into more detail on this because it’s important to understand the whitespace handling to use text blocks. Last, the compiler also translates escape sequences in text blocks. Let’s zoom in on whitespace handling. Looking at the source code for this text block, there are two types of whitespace. The first one is the essential whitespace, that is the whitespace that we use to indent inside of the text block and that we want to have preserved in the resulting string.
The whitespace shown by the dots here is indeed preserved in the resulting string. But then there’s also the incidental whitespace, which is only there because we laid out our source code this way. Most likely, we don’t want this whitespace to be part of the resulting string. And indeed, the whitespace shown here with the dots is actually removed by the compiler. In front of the text block, the compiler basically finds the longest sequence of whitespace that is common to all the lines of the text block in the source code. All trailing spaces for each line are also regarded incidental whitespace and are stripped when turning the text block into a string.
It’s almost as if the compiler tries to find the smallest bounding box for the text block and removes all other whitespace from the resulting string. Note that this analogy is not completely true because there’s still trailing whitespace inside of the bounding box here that is also stripped, but at least it gives some intuition for what the text block processing is doing. Sometimes you may want to use a text block to lay out multiple lines of text in your source code but still want to have all of the lines of a text block end up in a single line in the resulting string. You can do this by using a backslash as a sort of line continuation character.
It’s the syntax you may recognize from working with a shell or shell scripting. When you use backslashes like this, the resulting string contains all lines of the text block merged into a single string without any newline characters. We’ll now look at a demo of text blocks. But before we go there, I want to point out this text block guide, which is written by some JDK team members and contains some good practical advice on when to use text blocks and how to use them. Read it if you want to learn more details about text blocks before using them.
Demo: Text Blocks
Time to get hands on with text blocks. Let’s have a look what text blocks are like in real Java source code.
Here we have a simple main class containing the JSON as, let’s say, a legacy multiline string in source goes, including the explicit concatenation and the escape sequences inside of the string literals. I’m going to get rid of this old concatenated string sequence and replace it with a text block, which we’re going to do by opening with three double quotes, a new line, and the closing quotes are already inserted by the IDE automatically.
The cool thing is that I can now paste in this piece of JSON, and it will just work. We don’t have to think about escaping anything, and the new lines that are already there will be preserved. Let’s run this code to see with our own eyes that the result is true as expected. We see the JSON just as it was pasted into the source code.
public class TextBlocks {
public static void main(String... args) {
String jsonString = """
{
"firstName": "Alka",
"lastName": "%s"
}""".formatted("Patil");
System.out.println(jsonString);
}
}
Great. So what about this whitespace handling that we talked about? How does that work?
First, I’m going to decrease the indentation of the closing curly brace of the piece of JSON. If you look closely, you see a green line moving backward. This is a helpful way of the IDE indicating where the essential whitespace of a text block starts. If we change the text block like this, all whitespace now highlighted in orange will be preserved. Let’s also bring back the opening curly brace so that things are aligned again. I start playing around a bit with the closing delimiter of the text block because this is another way to influence where the essential whitespace of a text block starts. I’m going to move the closing delimiter to the next line and then start moving it to the left. Again, watch the green bar in the IDE move back.
If we lay out the text block like this, more whitespace will be considered essential, as highlighted again in orange. I encourage you to try this yourself to see how the whitespace handling of text blocks works in practice. For good measure, let’s print the text block as it is defined now, and we see that more whitespace is preserved in the resulting string. Now I’m going to introduce the backslash as a line continuation character. I’m going to add the backslash to the ending of the first three lines, which means that all four lines will be merged into a single line in the resulting string. We can verify this by running the code and observing that the string is now printed on a single line. Note, however, that the essential whitespace is still preserved and part of the resulting string.
It’s very convenient that we don’t have to escape the double quotes that are used for the keys and the values in the JSON, and that’s possible because text blocks are delimited by three double quotes, so double quotes on their own can be freely used within text blocks. But what if we wanted to have three double quotes inside of a text block? As you can see this breaks the code because the compiler now thinks that we’ve introduced an ending delimiter. To fix this, we can escape the first double quotes using a backslash, and the text block will now compile again with three consecutive double quotes. Finally, I want to show that you can also use formatting with text blocks. Java for a long time already had a string.format static method, where you pass in a string containing placeholders and an argument that will be formatted into the placeholders. But with text blocks, a new variation of this formatting method was added in the form of a formatted instance method on strings.
The text block contains the %s placeholder for my last name, and now I can call formatted and only have to pass in the argument that needs to be placed into the placeholder. When we run this code, the output is the same as before. Using the formatted method is a great way to do simple templating using text blocks. And this brings us to the end of this demo. Again, don’t hesitate to try this out yourself. It’s really instructive to play around with text blocks to see how they behave.
Text Blocks in Practice
Before we wrap up this article, I’d like to share with you a few use cases where I think that text blocks will be really useful just to give you some ideas where you might want to use them in the future.
One example is documentation of REST endpoints. If you’re using Swagger or OpenAPI annotations to annotate your endpoints, you probably know that you can provide a description for the endpoints that you’re implementing. This description will then be rendered into the OpenAPI or Swagger documentation, and it actually supports markdown. When you use text blocks here, you can easily see the markdown formatting. Using text blocks here seems like a natural fit. Another place where I believe that text blocks will be heavily used is in testing. For example, when you want to assert that some piece of JSON that you got from a method is actually equal to the JSON that you’d expect.
Using text blocks, you don’t have to worry about the newlines, the escaping, and it’s immediately clear that you’re looking at a valid piece of JSON here. Last, the formatted method that we saw in the demo allows you to use text blocks for simple templating needs so you can do simple substitutions without having to integrate a full‑fledged templating engine. When templates get more complicated, you still might want to do this, but for a lot of cases, using text blocks together with the formatted method is more than enough. I hope these example use cases inspire you to think about how you will use text blocks when moving to Java 15 or later.
Summary
By reading this article You now know enough about text blocks to start using them and have a much better experience of working with multiline strings in Java source code. The text blocks feature has been in preview for several Java releases, but now in Java 15 it is out of preview and we can start using it in production code. We learned about the processing steps that take place when the compiler turns a text block into a string, where the most important step was the whitespace processing. Distinguishing between essential and incidental whitespace is crucial. Of course, all of the theory doesn’t mean anything if we’re not going to use text blocks in our own code, so try out this feature yourself to get a feel for the mechanics, but also start thinking about use cases where text blocks can improve your code. In the next article, we’re also going to look at new language features in Java 15, but these are preview features. That means we can already find out what the future of Java holds. So let’s have a look at these preview features