In Java writing multi line strings is not a fun, you must have written code where requirement is to put HTML, JSON, XML or SQL query as String in your code.
See below example where we are trying to place JSON as String
String sampleJson = "{\"name\":\"josh\",\"salary\":\"8000$\",\"age\":27,\"isMarried\":\"True\"}";
here we have a very simple JSON with lot many escape sequence, it’s very hard to read.
we can do better to make it readable, see below
String sampleJsonReadableABit = "{" +
"\"name\":\"josh\"," +
"\"salary\":\"8000$\"," +
"\"age\":27," +
"\"isMarried\":\"True\""+
"}";
It’s better than earlier but still hard to read (lots of concatenation and escape sequences) and error prone. Adding or removing another attribute not easy for any developer.
Primary objective behind introducing Text Blocks is to
- Ease to write multi line strings
- Remove escape sequence and enhance readability
- Handle white spaces and indentation intelligently
What is Text Blocks?
Text blocks were released as preview feature with JDK 13 in June 2019 based on feedback this is previewed again as JEP 368 with JDK 14.
This feature missed by developer from a long time specially when someone has to read such code where JSON document or SQL statements are embedded.
As definition “**A Text Block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over the format when desired**”.
Text Block is defined with three double quotes “”” as opening and closing delimiter.
Let’s see how above JSON will be presented via Text Blocks
String jsonAsTextBlocks = """
{
"name":"josh",
"salary":"8000$",
"age":27,
"isMarried":"True"
}
""";
is it not better to read? No escape sequences :)
This feature is available with languages like Kotlin, Scala or Groovy.
Important Points
- Opening delimeter, must be followed by a line terminator before content actually starts, trying to write TextBlocks like below will be a compile time error.
String textBlockSample = """Ashish //Compile time error
""";
String textBlockSampleTwo = """Ashish"""; //Compile time error
String textBlockSampleThree = """"""; //Compile time error
String textBlockSampleFour = """ """; //Compile time error
!! In first example content placed just after opening delimiter “”” without line terminator. This is not allowed and will be a compile time error.
!! In second example also content placed directly between opening “”” and closing delimiter “”” without line terminator (Usually with string we write something like **String str =”Ashish”**). This is also not allowed and will be a compile time error.
Having space between opening delimiter and line terminator will simply be ignored by compiler as content will only begin after line terminator.
- Closing delimeter, no such rules. let's see some samples
String textBlockSample = """ //valid syntax
Ashish""";
String textBlockSampleTwo = """ //valid syntax
Ashish
""";
String textBlockSampleThree = """ //valid syntax (empty)
""";
- Incidental White Space, Text Block differentiates smartly differentiate between incidental white space from essential white space. The Java compiler automatically strips away the incidental white spaces
String htmlAsTextBlocks = """
<html>
<body>
<h1>
Sample Heading
</h1>
</body>
</html>
""";
If you run above code snippet output will be something like below,
<html>
...<body>
........<h1>
........Sample Heading
........</h1>
...</body>
</html>
any space before tag would be considered as incidental space and simply removed by compiler, but space which marked with **…** are not incidental such space will be considered as expected/desired/essential white space and respected by compiler.
If you want to explicitly add some space before content same can be controlled with closing delimiter “”” observe below snippet here closing delimiter has been moved left explicitly
String htmlAsTextBlocksWithSpace = """
<html>
<body>
<h1>
Sample Heading
</h1>
</body>
</html>
""";
it will have ouput like below:
<html>
<body>
<h1>
Sample Heading
</h1>
</body>
</html>
here leading spaces marked by closing delimiter respected by compiler. Note closing delimiter has a role to control leading white space but it can’t put any effect on trailing white space e.g. if above code change like below
String htmlAsTextBlocksWithSpace = """
<html>
<body>
<h1>
Sample Heading
</h1>
</body>
</html>
""";
it will have output like
<html>
<body>
<h1>
Sample Heading
</h1>
</body>
</html>
To specifically add some trailing white space, you can write something like:
String textBlockWithTrailingSpace = """
Ashish \040""";
above text block length if you calculate, it will be 11.
Similarities between Text Blocks and String
- Largely we can use Text Blocks wherever String usage is applicable e.g. if any method definition can take String variable as argument, we can pass TextBlock there.
private static void textBlocksAsString(String str){
System.out.println(str);
}
above method can be called as “**textBlocksAsString(“””
I am Fun
“””);**”
This is perfectly valid syntax.
- Traditional String values and text blocks are both compiled to the same type: String. The bytecode class file doesn’t distinguish whether a String value is derived from the traditional String or a text block. This implies that text block values are stored in the string pool as any normal String. Both == and equals will return true if they are referring to exactly same value. See below code snippet
String str = "Ashish";
String textBlock = """
Ashish""";
if you runs something
System.out.println(str==textBlock);
System.out.println(str.equals(textBlock));
Both statement will print true but if you slightly change the above code to:
String str = "Ashish";
String textBlock = """
Ashish
""";
both == and equals will return false.
- You can concatenate String and Text Blocks like you concatenate two Strings, see below
System.out.println( """
Works
is fun
"""
+
"Let's have some");
This is valid.
- For certain business requirement we have to write code where String need to split based on certain regular expression and convert into a List. Such use cases also got simplified with Text Blocks
String fruits = "Apple,Orange,Mango";
List<String> fruitsList = new ArrayList<>();
Collections.addAll(fruitsList,fruits.split(","));
//Such operation will be simplified like below
String fruitsTextBlocks = """
Apple
Orange
Banana
""";
List<String> lisOfFruits = fruitsTextBlocks.lines().collect(Collectors.toList());
Compilation Steps
Text Block processed by compiler in three steps:
Line Terminator — Windows and Linux has different line ending, windows use carriage return and line feed (“\r\n”) and Linux only use line feed (“\n”). To avoid any issues when source code transferred one OS to another, Text Blocks normalize line ending to \u000a. The escape sequences \n (LF), \f (FF), and \r (CR) are not interpreted during normalization; escape processing happens later.
Remove incidental white space — Above steps do normalization, in this steps all incidental white space will be remove as explained above. The escape sequences \b (backspace) and \t (tab) are not interpreted by the algorithm; escape processing happens later.
Interpret escape sequence — If your Text Block has any escape sequence it will be interpreted now. Text blocks support all of the escape sequences supported in string literals, including \n, \t, \’, \”, and \.
Escape Sequence
- Handling of escape sequence "
It’s perfectly valid to have escape sequence “ in Text Block, see below
String textBlockWithES = """
When your "work" speaks for yourself don't Interrupt
""";
but if you are trying to put it just adjacent to closing delimiter like below:
String textBlockWithESI = """
When your "work" speaks for yourself don't "Interrupt"""";
It will be a compile time error, it can be solved in two ways:
-
Take closing delimeter to next line.
String textBlockWithESI = """ When your "work" speaks for yourself don't "Interrupt" """;
Use escape
String textBlockWithESI = """
When your "work" speaks for yourself don't "Interrupt\"""";
This escape character \ can be placed before any “ among last four (“”””) in above line.
- Text Block can be embedded into another text block- if you are trying to put triple “”” in text block in any place other then opening and closing delimiter you have put escape character else it will be a compile time error:
String textBlockWithESII = """
When your "work" \""" speaks for yourself don't "Interrupt\"""";
//Above is a valid Text Block
On similar line if you need to embed another Text Block inside a text block you have to use escape character
String textBlockWithAnotherEmbedded =
"""
String text = \"""
A text block inside a text block
""\";
""";
### New Escape Sequences
- It’s a very common practice/requirement where a long string divided into multiple substring concatenated them with ‘+’ to maintain the code readability but output will be a single line. To handle similar requirement new escape sequence \ added in java this will only work with TextBlocks.
String quoteAsStr = "Fearlessness is like a muscle. " +
"I know from my own life that the more I exercise it, " +
"the more natural it becomes to not let my fears run me.";
System.out.println(quoteAsStr);
String quoteAsTextBlocks = """
Fearlessness is like a muscle. \
I know from my own life that the more I exercise it, \
the more natural it becomes to not let my fears run me.""";
System.out.println(quoteAsTextBlocks);
if you execute above code, both print output will be exactly same
Fearlessness is like a muscle. I know from my own life that the more I exercise it, the more natural it becomes to not let my fears run me.
Fearlessness is like a muscle. I know from my own life that the more I exercise it, the more natural it becomes to not let my fears run me.
- Another new escape sequence \s added with this JEP, is to simply translates to a single space (\u0020). Escape sequences aren’t translated until after incident space stripping, so \s can act as fence to prevent the stripping of trailing white space. This escape sequence applicable on both String and Text Blocks.
String colors = """
red \s""";
System.out.println(colors.length());
on console output will be 6.
New Methods
- String::stripIndent()
As we discussed above compiler removes incidental white spaces from Text Blocks, this method added in String to achieve same result with normal String.
String fruitsStr = "red\n green\n blue";
System.out.println(fruitsStr);
//1- It will print
//red
// green
// blue
System.out.println(fruitsStr.replace(" ","."));
//2- It will print
//red
//...green
//...blue
System.out.println(fruitsStr.stripIndent());
//3- It will print
//red
// green
// blue
In above snippet, you can observe there is no impact of stripIndent() method as there is no incidental white space found by compiler but if you have a string like below:
String fruitsStrWithLeadingSpace = " red\n green\n blue";
System.out.println(fruitsStrWithLeadingSpace);
//1- It will print
// red
// green
// blue
System.out.println(fruitsStrWithLeadingSpace.replace(" ","."));
//2- It will print
//..red
//...green
//...blue
System.out.println(fruitsStrWithLeadingSpace.stripIndent());
//3- It will print
//red
// green
// blue
Here you can see between second and third console print leading white space has been removed from String.
- String::translateEscapes()
As name implies this will translate escape sequence present in String, see below example
String fruitsStr = "red\n green\\n \"blue\"";
System.out.println(fruitsStr);
//1- It will print
//red
// green\n "blue"
System.out.println(fruitsStr.translateEscapes());
//2- And after translateEscape, it will print
//red
// green
// "blue"
you can observe that new line character has been translated in 2- console print.
- String::formatted(Object… args)
String jsonAsTextBlocks = """
{
"name":"%s"
}
""".formatted("Ashish");
System.out.println(jsonAsTextBlocks);
//It will print
//{
//"name":"Ashish"
//}
It work very similar to String.
Hope you guys have enjoyed this.
Disclaimer
It has been published before on medium https://medium.com/analytics-vidhya/text-blocks-jep-368-56c1625f97cf
Top comments (0)