BLOG.JETBRAINS.COM
Text Blocks in Java: Perfect for Multiline Strings
Youve likely used String variables to store values that span multiple lines, such as LLM prompts, JSON, HTML, XML, code snippets, and other such values.Some of these, such as a JSON value, include double quotes as part of the data. Imagine the inconvenience of using backslashes (\) to escape those quotes, indenting lines using newlines, tabs, or spaces, and adding a concatenation operator at the end of each line. Coding such string values is a nightmare. The resulting string is not just hard to write, but also hard to read. Language-specific errors, like a missing comma in a JSON value, can easily creep in.Dont worry, theres already a solution. Java 15 introduced Text Blocks, multiline strings that make it easier to define data that spans multiple lines. Text Blocks remove the need for concatenation operators or escape sequences when working with HTML, XML, JSON, or SQL queries stored as strings. The values are easier to read, and its simpler to spot issues like missing spaces in SQL queries or a missing comma in a JSON value.Lets understand the benefits of using Text Blocks with an example.An example what are the existing pain pointsImagine you need to store the following JSON text in your Java code:{"name": "Sonam Wangchuk""movement": "#ILiveSimply","result": "Let planet simply live"}This JSON value can be stored as a multi line String value (without using a TextBlock) as follows:String myJson = "{\n" +" \"name\": \"Sonam Wangchuk\"\n" +" \"movement\": \"#ILiveSimply\",\n" +" \"result\": \"Let planet simply live\"\n" +"}";Writing the preceding code manually can be a nightmare. Escape characters and concatenation operators make it hard to write and read. To include double quotes within a string, you must escape them using a backslash (since is also a string delimiter). To preserve the formatting of the JSON object, you need to add whitespace such as new lines, tabs, or spaces.With all that formatting overhead, you probably missed that the JSON above is missing a comma at the end of the first line. This missing comma can cause a parsing error later if you try to convert the string into a JSON object.Lets see how Text Blocks can help.Using Text BlocksTextBlocks are multiline Strings (their type is java.lang.String). By using Text Blocks, you can store the previous String value, as follows:String myJson = """{"name": "Sonam Wangchuk""movement": "#ILiveSimply","result": "Let planet simply live"}""";Text Blocks are simple to create, read, and edit. They eliminate the need for concatenation operators and (most) escape sequences when working with String values that span more than one line, as shown below:The next section covers the syntax details of text blocks. If youre already familiar with them, feel free to skip ahead.Syntax of TextBlocksHere are a couple of syntax rules to follow when you are working with Text Blocks.Opening and closing delimiter """Unlike the single double quotes (") used for regular String values, Text Blocks use three double quotes (""") as their opening and closing delimiters. The opening delimiter can be followed by zero or more whitespaces, but it must be followed by a line terminator. A Text Block value begins after this line terminator.If a Text Block doesnt include a newline character immediately after the opening """, IntelliJ IDEA can detect this and prompt you to correct it:Incidental white spacesWhat rules does the compiler follow to include or exclude leading and trailing whitespace in a Text Block? Before we answer this question, lets first understand what whitespaces are. When we talk about a whitespace in Java Text Blocks, it can refer to different types of characters, such as:A space The standard space character we use to separate wordsTabs The popular Tab characters, that is, ('\t'). Wars have been fought over whether to use tabs or space to indent code :)Line breaks Newline characters ('\n' on Unix/Linux/macOS, or '\r\n' on Windows)Carriage returns ('\r')First, lets talk about how the leading white spaces are handled in a Text Block.Leading spacesWhy do you need leading spaces? You would usually add tabs or spaces to values, such as a JSON, to align them vertically in your code. In Text Blocks, the leftmost non-whitespace character on any of the lines or the leftmost closing delimiter defines where meaningful white space begins. IntelliJ IDEA helps you view this position using a vertical line a feature that I absolutely love about Text Blocks support in IntelliJ IDEA.Heres how the vertical bar in IntelliJ IDEA lets you visualize the starting position of your Text Block values:Just in case you cant view the vertical green line shown in the preceding image, use Shift+Shift, Find Show indent guides, and enable it in IntelliJ IDEA.The following image shows another way to understand which leading spaces are included in your text blocks blue rectangles represent the spaces that are not part of your textblock and the light green rectangles represent the leading spaces that are included in your text block:If you move the closing triple quotes to the left, the white spaces included in the textblock changes, as shown in the following image:Trailing white spacesBy default, the trailing white spaces are removed in Text Block values. IntelliJ IDEA can detect when you add trailing white spaces in your textblocks. It would highlight those spaces (to ensure you didnt add them by mistake).When you click Alt + Enter, it could prompt you to either Escape trailing whitespace characters, or Remove trailing whitespace characters. If you choose the former option, IntelliJ IDEA will add \s at the end (\s represents a single space), as shown in the following gif:Where would you use a trailing white space?Imagine you are using a method from a library that reads the first 40 characters of a line to extract two values from it, and store it in a Map, as follows:public Map<String, String> parseFixedWidthData(String fixedWidthData) {Map<String, String> result = new HashMap<>();String[] lines = fixedWidthData.split("\n");for (String line : lines) {String field1 = line.substring(0, 19).trim();String field2 = line.substring(20, 39).trim();result.put(field1, field2);}return result;}If you are using a textblock to pass value to the method parseFixedWidthData, you should define it as follows, escaping the trailing whitespaces, so the the preceding method doesnt throw an IndexOutOfBounds exception:String fixedWidthData = """CUSTOMER_NAME JOHN DOE \sACCOUNT_NUMBER 12345678-9879 \sAGE 45 \s""";Continuation char \When you place your text on a new line in a text block, a new line char is added to your String value. Imagine using a textblock to store a store long URL so that it is easy to read, as follows:String apiUrl = """ https://www.alamy.com/stock-photo-abstract-geometric-pattern-hipster-fashion-design-print-hexagonal-175905258.html? imageid=0DF26DE9-AC7B-4C78-8770-E1AC9EC8783A &p=379271 &pn=1 &searchId=8cf93ae4926578c6f55e3756c4010a71&searchtype=0""";However, if you use the preceding text block to connect to a URL and retrieve a response, the code will throw an exception. Inclusion of \n in the URL makes it an invalid URL. To address it, you can use the continuation character, that is, \ at the end of a line in your text block (so that the resulting string doesnt include a new line character):String apiUrl = """ https://www.alamy.com/stock-photo-abstract-geometric-pattern-hipster-fashion-design-print-hexagonal-175905258.html?\ imageid=0DF26DE9-AC7B-4C78-8770-E1AC9EC8783A\ &p=379271\ &pn=1\ &searchId=8cf93ae4926578c6f55e3756c4010a71&searchtype=0""";More about TextBlocksWith the syntax rules under your belt, lets learn more about Text blocks.Not a String variationJava isnt adding a variation of type String with Text Blocks. They are compiled to regular String instances (java.lang.String). You can think of Textblocks as syntactic sugar that allows you to write Strings without using the concatenating operators and escape sequences. If you decompile a class that defines a text block, youll see that they are compiled to regular strings with single pair of double quotes as the delimiter, as shown in the following gif (the top bar mentions that you are viewing a Decompiled .class file):Call any String method on a text blockSince there is just one java.lang.String type (not a variation for Text blocks), it means that you can call all String methods on text blocks:Convert a text block to a regular stringImagine you are migrating your codebase to a development environment that doesnt support Textblocks (Java 14 or earlier versions). In such case, you can invoke Context Actions to convert a Text Block to a regular String literal:Language Injections in TextblocksInjecting a language into Text Blocks in IntelliJ IDEA enables syntax highlighting and real-time error detection, helping to catch issues such as unclosed JSON values or HTML tags, missing or mismatched quotes in attributes, inconsistent indentation, and unescaped special characters. You also get IntelliJ IDEAs support like code completion, and value validation.The following gif shows how you can inject JSON as a language in a text block (language injection in IntelliJ IDEA applies to regular strings too):As you can see, the language injection option enables you to choose from multiple options (including JSON).Practical examples where to use Text BlocksApart from using Textblocks to store JSON data (as shown in the preceding sections), you can think of using Text Blocks to store values that usually span multiple lines such as XML, HTML data, or code snippets written in other programming languages. This section highlights the practical examples where you can use text blocks.1. ASCII ArtYou can use textblock to store and output ASCII art, such as the following:String textblock = """ """;2. Logging dataImagine while working with an online shopping application, you need to log a message with order details, if the quantity for a product in an order is 0 or negative. It is common to create a String that includes literals, such as, Invalid order, and order details that can be accessed using variables like orderId, etc. Heres a sample code to accomplish this (focus on the concatenated String):public void processOrder(int orderId, String product, int qty, LocalDate orderDate) {if (qty <= 0) {String errorMessage = "Invalid order quantity:" + qty +"for product" + product + ",order ID" + orderId;logger.error(errorMessage);return;}//.. Remaining code}The code seems harmless. However, Ive often missed adding spaces before and after the literal text values in similar code, generating a log message similar to the following that is hard to read:Invalid order quantity: -5for productWidget,order ID12345A safer bet would be to use textblocks for this logging message that can help you spot the missing spaces. Even if you miss adding spaces, the new line characters can space out the log messages:public void processOrder(int orderId, String product, int qty, LocalDate orderDate) {if (qty <= 0) {String errorMessage = ("""Invalid order quantity:%dfor product %s,order ID %d""").formatted(qty, product, orderId);logger.info(errorMessage);System.out.println(errorMessage);return;}//.. Remaining code}3. XML or HTML dataHeres an example of a Text Block storing a HTML value:String html = """<HTML><BODY><P>Stop generating 6 million tons of plastic waste</P><UL><LI>Keep a spoon, fork, knife in your bag.</LI><LI>Avoid using single use plastic cutlery.</LI></UL></BODY></HTML>""";4. Complex JSON dataIn the beginning of this blog post, I covered how text blocks can help eliminate the clutter. The clutter increases manifolds, when you start working with more complex JSON objects, as follows:String json = "{\n" +" \"cod\": \"200\",\n" +" \"city\": {\n" +" \"id\": 524901,,,,\n" +" \"name\": \"GreatCity\",\n" +" \"country\": \"AwesomeCountry\",\n" +" \"coord\": {\n" +" \"lat\": 55.7522,\n" +" \"lon\": 37.6156\n" +" }\n" +" }\n" +"}";With textblocks, the cognitive load reduces, as you can see in the following code snippet:String json = """{"cod": "200","city": {"id": 524901,,,,"name": "GreatCity","country": "AwesomeCountry","coord": {"lat": 55.7522,"lon": 37.6156}}}""";Perhaps you can inject language in the preceding text block and determine the syntax errors with the JSON value.5. Multiline String valuesHeres just a long line of String, stored using Text Blocks:String aLongString = """I'm a long String value, which can't fit on aSingle line."Hey!", would you prefer a cup of coffee?"Yes, please".""";Text Blocks take off the visual clutter from multiline strings which existed in the form of concatenation operators and escape sequences.6. SQL QueriesImagine using the following code to store a SQL query:String query ="SELECT name, age" +"FROM EMP" +"WHERE name = \'John\'" +"AND age > 20";The preceding code represents an invalid query. Due to missing spaces at the end of each line, this query will be interpreted as the following:SELECT name, ageFROM EMPWHERE name = 'John'AND age > 20You can address these issues by using text blocks:String query = """SELECT name, ageFROM EMPWHERE name = 'John'AND age > 20""";7. Email templates multiline string values with literal and variable valuesWhen concatenating string literals with variable values, it is easy to miss adding a single space in string literal, right before or after a variable value. It could result in poorly formatted output, or output that is not-so-readable. It could also result in displaying output you didnt expect due to those missing spaces. Consider the following code that uses a combination of string literals and variable values to send a text to a customer:String username = "Alice"; String topic = "Java Records"; String previousContext = "We were discussing immutable data classes."; String email = "Hi" + username + ",\n\n" + "Let's continue our discussion about " + topic + ".\n" + "For context, " + previousContext + "\n\n" + "Can you tell me more about what specific aspects of" + topic + "you're interested in?";You could use TextBlock and formatted(), so that the variable substitution is cleaner:String email = """ Hi %s, Let's continue our discussion about %s. For context, %s Can you tell me more about what specific aspects of %s you're interested in? """.formatted(username, topic, previousContext, topic);8. Creating simple billsYou can create simple bills (such as the following) to print using textblocks:-------------------------------------------------------------------------------------- Your Neighbourhood Art Supplies Store--------------------------------------------------------------------------------------Date: 2023-10-20 Invoice Number: 12345 Customer DetailsName: John Smith Address: 123 Main Street City: Smallville Phone: 555-123-4567 --------------------------------------------------------------------------------------S.No. Item Name Quantity Unit Price($) Total($)--------------------------------------------------------------------------------------1 Acrylic Paint Set 1 20.00 20.002 Watercolor Brushes 5 15.00 75.003 Sketchbook 12 10.00 120.004 Oil Paints Set 1 25.00 25.005 Canvas Panels (5-pack) 6 12.00 72.00--------------------------------------------------------------------------------------Subtotal: $82.0Sales Tax (6%): $4.92Total Amount: $86.92;-------------------------------------------------------------------------------------- Thank you for shopping with us!--------------------------------------------------------------------------------------Code Migrations using text blocks instead of a regular stringThe release of Java 25, the next LTS version, is around the corner. If you plan to migrate your existing codebases using JDK version 14 or earlier to a newer version, you can start using Text Blocks in your code.To migrate all eligible multiline String values currently stored across multiple lines using concatenation operators to Text Blocks, you can proceed in two ways. The first approach is to run the inspection Text blocks can be used on your entire project or selected directories. In the Problems view window that opens, you can apply these changes individually or in a batch.To demonstrate this feature, I forked an open-source project from GitHub, JSON-java, and ran the inspection Text blocks can be used, as shown in the following GIF:The second approach is to create a new profile in Settings, say, Migrate to 24, and add all the migration inspections to this profile. Then, you can execute the Inspect Code command and run this inspection profile on your codebase. Use the Problems view window to accept multiple changes at once or review them individually.SummaryText blocks in Java are syntactic sugar to make it easy for you to create string values that span multiple lines, without needing to use concatenation operators or escape sequences. This makes it easier to read and write such values, reducing cognitive load for us developers. Since the values are clutter-free, you can also spot syntax errors in these multiline values, such as a missing quote or comma. By injecting a language or a reference into these text blocks, IntelliJ IDEA can help you further by highlighting these errors and even suggesting how to fix them.Text blocks start and end with three double quotes. By default, trailing whitespaces are ignored in text blocks. To includeor in other words, escapethe trailing whitespaces, use \s. To join two lines, add a backslash (\) at the end of the first line.Text blocks are quite useful when youre working with data that usually spans multiple lines, such as JSON, SQL queries, HTML, XML, and others. You could use text blocks to output beautiful line art, format log messages, or even generate simple bills for your neighbourhood stores.The release of Java 25 is around the corner. If youre still working with an older version of the JDK, such as 8 or 11, I recommend moving to a newer version so you can benefit from newer features like text blocks.Happy coding!
0 Comments
0 Shares
28 Views