Java - String Template Processors (STR, FMT, RAW) Examples

This tutorial shows you how to use the StringTemplate feature introduced in Java 21, including the STR, FMT, and RAW processors..

When writing code, it's quite common to construct a string by combining expressions. In Java, there are several ways to do it. You can use string concatenations, StringBuilder, String::format or String::formatted, and MessageFormat. However, they all have drawbacks as explained in the code below.

  int a = 100;
  int b = 2;
  
  // String concatenation may produce hard-to-read code, especially if there are many concatenations
  // It's also has worse performance.
  String str1 = a + " + " + b + " = " + (a + b);
  System.out.println(str1);
  
  // StringBuilder is too verbose.
  String str2 = new StringBuilder()
      .append(a)
      .append(" + ")
      .append(b)
      .append(" = ")
      .append(a + b)
      .toString();
  System.out.println(str2);
  
  // String::format and String::formatted may cause arity and type mismatches.
  String str3a = String.format("%1$d + %2$d = %3$d", a, b, a + b);
  String str3b = "%1$d + %2$d = %3$d".formatted(a, b, a + b);
  System.out.println(str3a);
  System.out.println(str3b);
  
  // MessageFormat requires too much ceremony
  MessageFormat mf = new MessageFormat("{0} + {1} = {2}");
  Object[] args = {a, b, a + b};
  String str4 = mf.format(args);
  System.out.println(str4);

You may already know that some other programming languages support defining string templates that contain embedded variables or expressions. Finally, Java 21 introduced a similar feature. It's called StringTemplate and is available in the java.lang package.

Template Expression

A string template expression consists of three parts.

  1. The processor name (STR, FMT, or RAW)
  2. A dot character (U+002E)
  3. The string template which can contain multiple embedded expressions.

The first part indicates the processor name. Java provides some built-in processors which include STR, FMT, and RAW. The differences between them are going to be explained later. The name is followed by a dot character.

A string template starts and ends with " (double quotes). Alternatively, it can use """ (triple double quotes) to allow defining a template in multi-line like a text block. It can contain one or more embedded expressions. An embedded expression starts with \ (backslash), followed by an { (opening curly brace), the expression, and a } (closing curly brace). The expression itself can be a variable or a more complex expression. Writing the expression is the same as it would be written outside the template expression. If there are multiple embedded expressions, it will be executed in order from left to right.

Using STR Template Processor

Java's StringTemplate class provides a template processor instance called STR, which can perform string interpolation of a given string template. It actually uses the StringTemplate.interpolate method. However, for a better readability. it's recommended to use the STR rather than invoking the interpolate method.

Access a Variable

The STR processor is defined in the java.lang.StringTemplate class. However, it's not required to import it since Java is able to recognize it without the need to add an explicit import.

In the example below, there is a simple expression STR."Created by \{name}". The embedded expression is a variable which has already been defined before. If the embedded expression is invalid (e.g. if you use an undefined variable), you will get a compile-time error.

  String name = "Woolha.com";
  String text = STR."Created by \{name}";
  System.out.println(text);

Output:

  Created by Woolha.com

Below is another example which contains multiple embedded expressions that use and modify the same variable. The execution order is from left to right. As a result, the execution of an embedded expression may affect the next ones.

  int count = 1;
  String data = STR."\{count++}, \{count++}, \{count++}, \{count++}";
  System.out.println(data);

Output:

  1, 2, 3, 4

Invoke a Method

The embedded expression can also contain a call to a method in order to use the returned value. For example, we have the following method.

  private String convertToUpperCase(String value) {
    return value.toUpperCase();
  }

Below is an example of an embedded expression that calls the method above. As you can see, the method is invoked with an argument whose type is a String. Double quotes character is supported and it must be written as-is without being escaped. In other words, you can write an embedded expression exactly as you would have written it outside the template expression.

  String text = STR."The value is \{convertToUpperCase("foo")}";
  System.out.println(text);

Output:

  The value is FOO

The called method must have a return value. It's not allowed to call a void method in the embedded expression. Below is an invalid example that calls the System.out.print method.

  String text = STR."The value is \{System.out.print("foo")}";
  System.out.println(text);

Output:

  error: 'void' type not allowed here
    String text = STR."The value is \{System.out.print("foo")}";

Access a Class Property

It's also possible to access any property of a class as long as the access modifier allows it. For example, the Name class below has two private fields firstName and lastName along with the public getter methods.

  public class Name {
  
    private String firstName;
  
    private String lastName;
  
    public Name(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
    }
  
    public String getFirstName() {
      return this.firstName;
    }
  
    public String getLastName() {
      return this.lastName;
    }
  }

If there is an instance of Name, it's possible to access the getFirstName and getLastName properties since they're public. You cannot access the firstName and lastName fields directly since they're private.

  Name name = new Name("Ivan", "Cute");
  String text = STR."My full name is \{name.getFirstName()} \{name.getLastName()}";
  System.out.println(text);

Output:

  My full name is Ivan Cute

Nested Embedded Expression

An embedded expression may contain another embedded expression, as shown in the example below.

  String[] types = { "grass", "water", "fire" };
  String s = STR."\{types[0]}, \{STR."\{types[1]}, \{types[2]}"}";
  System.out.println(s);

Output:

  grass, water, fire

Multi-line Embedded Expression

Sometimes, the embedded expression can be quite long to be written in one line. In that case, it's better to spread it over multiple lines. If you do that, Java will interpolate the expression and the result will be put in the same line as the start of the embedded expression without adding newlines.

  String text = STR. "Order will be expired at \{ZonedDateTime.now()
      .truncatedTo(ChronoUnit.DAYS)
      .plusDays(1)
  }";

Output:

  Order will be expired at 2023-08-19T00:00+07:00[Indian/Christmas]

Multi-line Template Expression

The template itself can be spreaded over multiple lines if necessary. The syntax is similar to Java's text blocks which uses triple double quotes at the start and end.

  String name = "Cutie";
  double amount = 100;
  String html = STR."""
     <div class="content>
       <p>Dear \{name}</p>
       <p>Your order of $\{amount} has been paid</p>
     </div>
     """;
  System.out.println(html);

Output:

  <div class="content>
    <p>Dear Cutie</p>
    <p>Your order of $100.0 has been paid</p>
  </div>

Using FMT Template Processor

There is another processor called FMT, which constructs a string by combining Java's Formatter and StringTemplate. It uses the value from the embedded expression placed right after the format specifier.

The processor is defined in the FormatProcessor class of the java.util package. To use the FMT processor, it's necessary to add the following import.

  import static java.util.FormatProcessor.FMT;

For example, we have the following record type.

  record Player(int idNumber, String name, double score) {}

There are some instances of the record and we want to print each instance in a different row, with the values of each column neatly aligned. We can use Formatter's capability to pad and format the values of each field. Each value to be formatted can be written as an embedded expression following the format specifier.

  Player[] players = new Player[] {
      new Player(1, "Foo", 8123.112),
      new Player(20, "Bar", 211.56),
      new Player(300, "Baz", 22.5671),
  };
  String formattedPlayers = FMT."""
    No     Name     Score
    %-5d\{players[0].idNumber}  %-5s\{players[0].name}  %7.2f\{players[0].score}
    %-5d\{players[1].idNumber}  %-5s\{players[1].name}  %7.2f\{players[1].score}
    %-5d\{players[2].idNumber}  %-5s\{players[2].name}  %7.2f\{players[2].score}
    \{" ".repeat(9)} Avg %7.2f\{players[0].score + players[1].score + players[2].score}
    """;
  System.out.println(formattedPlayers);

Output:

  No     Name     Score
  1      Foo    8123.11
  20     Bar     211.56
  300    Baz      22.57
            Avg 8357.24

Using RAW Template Processor

There is another processor instance called RAW, which is used to create a template with deferred processing. That means the defined template is not interpolated automatically. To perform the interpolation, you have to call Processor.process(StringTemplate) or StringTemplate.process(Processor).

You have to add the following import to use RAW.

  import static java.lang.StringTemplate.RAW;

Below is an example. First, we create a template expression using the RAW processor, which returns a StringTemplate. Then, to perform the interpolation, call the process method of a Processor by passing the StringTemplate. The Processor itself can be either STR or FMT. Another alternative is by using the process method of the StringTemplate with the Processor to use passed as the argument.

  String name = "Woolha.com";
  StringTemplate template = RAW."Created by \{name}";

  String text = STR.process(template);
  System.out.println(text);

  String text2 = template.process(STR);
  System.out.println(text2);

Output:

  Created by Woolha.com
  Created by Woolha.com

Summary

The StringTemplate class in Java provides a convenient way to define a string template that's easy to use and read. A template can have one or more embedded expressions which can be as simple as a variable or a complex expression. Java provides built-in processors which include STR, FMT, and RAW.