Java 14 features: Text Blocks & Foreign-Memory Access API

This is the fourth and last post in the blog post series I wrote covering the features that has been added to java 14, released just a couple of days back.

In this post, I will cover 2 more features: Text Blocks (Second Preview) and Foreign-Memory Access API (Incubator).

Java 14 rew features articles:

JEP 368: Text Blocks (Second Preview)

The first preview of Text Blocks was introduced in Java 13 as a new, more concrete and concise vision for how Raw String Literals should work in Java. You can read more about the withdraw of JEP 326 here.

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, make inline multi-line Strings more readable and gives the developer control over the format when desired.

Usage

Text Blocks starts with three double quote characters ("""), continues with zero or more space, tab, and form feed characters, and concludes with a line terminator. The closing delimiter is a sequence of three double quote characters (""").

"""
Text  
Block  
Example  
"""

Optionally, the closing delimiter can appear in line on the closing line:

"""
Text  
Block  
Example"""  

Note that the result type of a text block is still a String. Text blocks just provide us with another way to write String literals in our source code.

Indentation

Text Blocks made it a bit easier to indent our code properly. To calculate how many white space characters should be removed from every line, the compiler determines the line with the least white space characters and then shifts the complete text block to the left. The compiler takes whitespace indentation into consideration, differentiating incidental whitespaces from essential whitespaces.

For example, including the trailing blank line with the closing delimiter, the common white space prefix is 11, so eleven white spaces are removed from the start of each line.

// spaces (dots) will be removed
        String text= """
...........     some text
...........     having fun
...........     with Text Blocks
...........""";

Now, suppose the closing delimiter is moved slightly to the right of the content, in this case 16 white spaces are removed from the start of each line:

// spaces (dots) will be removed
        String text= """
................some text
................having fun
................with Text Blocks
................   """;

The spaces visualized with dots are considered to be incidental and hence will be removed.

Escaping

The use of the escape sequences \" and \n is permitted in a text block, but not necessary or recommended. However, representing the sequence """ in a text block requires the escaping of at least one " character, to avoid mimicking the closing delimiter.

String code =  
    """
    String text = \"""
        This is a Text Block inside a Text Block
    \""";
    """;

The string represented by a text block is not the literal sequence of characters in the content. Instead, the string represented by a text block is the result of applying the following transformations to the content, in order:

  • Line terminators are normalized to the ASCII LF, character, as follows:
    • An ASCII CR (Carriage Return) character followed by an ASCII LF (Line Feed) character is translated to an ASCII LF character.
    • An ASCII CR character is translated to an ASCII LF character.
  • Incidental white space is removed, as if by execution of String::stripIndent on the characters resulting from step 1.
  • Escape sequences are interpreted, as if by execution of String::translateEscapes on the characters resulting from step 2.
New escape sequences

With Java 14, escaping in text blocks got 2 more features:

  • The \<line-terminator> escape sequence explicitly suppresses the insertion of a newline character. Very useful when you have long lines of text in the source code that you want to format in a readable way.
  • The new \s escape sequence simply translates to a single space (\u0020). Which basically tells the compiler to preserve any spaces in front of this escaped space, instead of ignoring them (default behaviour).

Methods

To support the new features of Text Blocks, a couple of methods have been introduced (some of them already mentioned above):

  • String::stripIndent(): used to strip away incidental white space from the text block content
  • String::translateEscapes(): used to translate escape sequences
  • String::formatted(Object... args): simplify value substitution in the text block

JEP 370: Foreign-Memory Access API (Incubator)

This incubating feature enables efficient, safe and deterministic access to native memory segments out of JVM heap space (off-heap). The JEP also states that this foreign memory API is intended as an alternative to currently used approaches (java.nio.ByteBuffer introduced in 2002 with Java 1.4 and sun.misc.Unsafe way before).

The foreign-memory access API, part of Project Panama, introduces three main abstractions:

  • MemorySegment: is used to model a contiguous memory region with given spatial and temporal bounds.
  • MemoryAddress: can be thought of as an offset within a segment.
  • MemoryLayout: a way to define the layout of a memory segment in a language neutral fashion.

To start playing with this API, You need first to add jdk.incubator.foreign module manually and enable preview features.
The simple example below allocates 10 bytes memory out of JVM heap space and prints its base address.

import jdk.incubator.foreign.MemoryAddress;  
import jdk.incubator.foreign.MemorySegment;

public class FmaExample {  
    public static void main(String[] args) {

        MemoryAddress address = MemorySegment.allocateNative(4).baseAddress();
        System.out.print(address);
    }
}

// Prints
WARNING: Using incubator modules: jdk.incubator.foreign  
MemoryAddress{ region: MemorySegment{ id=0x1406e03c limit: 4 } offset=0x0 }  

In the above, we are using the overloaded allocateNative() which takes a long value of the size in bytes and create a new native memory segment that models a newly allocated block of off-heap memory. There are two other versions of this method, one which accepts a MemoryLayout and one which accepts a size in bytes and the byte alignment.

In order to use the memory segment from the example above, memory-access var handle should be used. They are obtained using factory methods in the MemoryHandles class. The example below stores 10 bytes as int at the base of the off-heap memory segment:

import jdk.incubator.foreign.MemoryAddress;  
import jdk.incubator.foreign.MemoryHandles;  
import jdk.incubator.foreign.MemorySegment;  
import java.lang.invoke.VarHandle;  
import java.nio.ByteOrder;

public class FmaExample {  
    public static void main(String[] args) {

        MemoryAddress address = MemorySegment.allocateNative(10).baseAddress();
        VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
        handle.set(address, 10);

        System.out.println("Memory Value: " + handle.get(address));
    }
}

// Prints
WARNING: Using incubator modules: jdk.incubator.foreign  
Memory Value: 10  

Ressources and further reading: