Upcoming news from Java 18

Java 18 is scheduled to be released in the first quarter of 2022. However, there’s already a list of JDK Enhancement Proposals (JEPs). In this article, we will have an overview of the most interesting ones.

UTF-8 by Default – JEP 400

How to know Java’s default Character Set ? the obvious answer is via:

Charset.defaultCharset()

The default Character set varies, depending on your Operating System. On some OS, like Windows, it depends upon the user’s locale and the default encoding, . On macOS, it is UTF-8 except in the POSIX C locale.

A quick hack to check the default charset is to run Java with the following parameters:

java -XshowSettings:properties -version 2>&1 | grep file.encoding

JEP 400 proposes to change the specification of Charset.defaultCharset() to set as default charset UTF-8 unless configured otherwise by an implementation-specific means.

By making UTF-8 the charset Java programs can be more predictable and portable.

Developers will be able to check for issues by running in advance of any early-access or GA Java 18 release the following option:

-Dfile.encoding=UTF-8

Simple Web Server – JEP 408

The proposal is to include an out-of-the-box Web server via command line. The goal is not to provide advanced features such as authentication, access control, or encryption. You can use the built-in Web server tool for local tests or file-sharing purposes.

Here is how the command line tool should work:

$ java -m jdk.httpserver

Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /cwd and subdirectories on 127.0.0.1 port 8000
URL: http://127.0.0.1:8000/

And here is how to change the default port with the option “-p”:

$ java -m jdk.httpserver -p 9000

You can also manage the Web server programmatically. Here’s as an example, how to use JShell Command Line to bind the Web Server on Port 8080:

jshell> var server = SimpleFileServer.createFileServer(new InetSocketAddress(8080),
   ...> Path.of("/some/path"), OutputLevel.VERBOSE);
jshell> server.start()

Code Snippets in Java API Documentation – JEP 413

With the current Java implementation, there is no standard way to include code snippets in the Java Doc. This proposal aims at introducing the inline tag @snippet to declare code fragments to appear in the generated documentation.

Code fragments are usually Java source code, but they may also be also source code in other languages, fragments of properties files, or plain text

Here is an example of how the @snippet tag should work:

/**
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */


Reimplement Core Reflection with Method Handles – JEP 416

Firstly, no panic! This JEP is not intended to make any change to the java.lang.reflect public API but it is purely an implementation change.

The proposal is to introduce Method Handles in the implementation of java.lang.reflect.Method, Constructor, and Field.

In a nutshell, Method Handles are a low-level mechanism for finding, adapting and invoking methods by using optional transformations of arguments or return values.

Making Method Handlesthe underlying mechanism for reflection will reduce the maintenance and development cost of both the java.lang.reflect and java.lang.invoke APIs.

If your code depends upon highly implementation-specific aspects of the existing implementation there can be impacts though. Also, method-handle invocation may consume more resources than the old core reflection implementation. To mitigate this compatibility risk, as a workaround you can enable the old implementation by using:

-Djdk.reflect.useDirectMethodHandle=false.


Vector API – JEP 417

The Vector API aims to improve the performance of vector computations. A vector computation consists of a sequence of operations on vectors.

This API leverages the existing HotSpot auto-vectorizer but with a user model, which makes the computation more predictable and robust.

The proposed API includes at the top the the abstract class Vector<E> . The type variable E is instantiated as the boxed type of the scalar primitive integral or floating point element types covered by the vector.

A vector also has a shape which defines the size, in bits, of the vector. The combination of element type and shape determines a vector’s species, represented by VectorSpecies<E>.  As an example, here is a simple scalar computation over elements of arrays:   

void scalarComputation(float[] a, float[] b, float[] c) {
   for (int i = 0; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
   }
}

This is the counterpart using the Vector API:

static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

void vectorComputation(float[] a, float[] b, float[] c) {
    int i = 0;
    int upperBound = SPECIES.loopBound(a.length);
    for (; i < upperBound; i += SPECIES.length()) {
        // FloatVector va, vb, vc;
        var va = FloatVector.fromArray(SPECIES, a, i);
        var vb = FloatVector.fromArray(SPECIES, b, i);
        var vc = va.mul(va)
                   .add(vb.mul(vb))
                   .neg();
        vc.intoArray(c, i);
    }
    for (; i < a.length; i++) {
        c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
    }
}

In the above example, a VectorSpecies, whose shape is optimal for FloatVector, has been stored in a static final field so that the runtime compiler treats the value as constant and can therefore better optimize the vector computation.

The main loop then iterates over the array parameters in strides of the vector length (that is., the species length). It loads two FloatVector of the given species from arrays a and b at the corresponding index, fluently performs the arithmetic operations, and then stores the result into array c.

If any array elements are left over after the last iteration then the results for those tail elements are computed with an ordinary scalar loop.  

Internet-Address Resolution SPI – JEP 418

The java.net.InetAddress API resolves host names to Internet Protocol (IP) addresses, and vice versa. In the current implementation, this API uses the OS’s native resolver (typically configured with a combination of a local hosts file and the DNS).
This proposal defines a service-provider interface (SPI) for host name and address resolution, so that java.net.InetAddress can make use of resolvers other than the platform’s built-in resolver.
The new InetAddress API will use a Service Loader to locate a resolver provider.

The built-in implementation will be used as before when no providers are found.
To manage this SPI, you will use one of the following classes within the java.net.spi package:

  • InetAddressResolverProvider — an abstract class defining the service to be located by java.util.ServiceLoader.
  • InetAddressResolver — an interface that defines methods for the fundamental forward and reverse lookup operations.
  • InetAddressResolver.LookupPolicy — a class whose instances describe the characteristics of a resolution request,
  • InetAddressResolverProvider.Configuration — an interface describing the platform’s built-in configuration for resolution operations.

Foreign Function & Memory API – JEP 419

The Java Platform has a significant amount of libraries to reach non-JVM platforms. For example, it is possible to reach RDBMS with JDBC Drivers. It is also possible to invoke web services (HTTP client) or serve remote clients (NIO channels), or communicate with local processes using Sockets.

However, Java still has significant pitfalls in accessing code and data on the same machine which runs outside the Java runtime. It is true that Java Native Interface (JNI) supports the invocation of native code , yet it is inadequate for many reasons:

  • Firstly, JNI involves several tedious artifacts: a Java API (to wrap native methods), a C header file derived from the Java API, and a C implementation that calls the native library of interest.
  • Then, JNI can only interoperate with libraries written in languages that adopt some calling conventions – typically C and C++..
  • Finally, JNI does not reconcile the Java type system with the C type system (e.g. Java represents data as objects while C represents data with structs)

The Foreign Function & Memory API (FFM API) defines classes and interfaces so that client code in libraries and applications can

  1. Allocate foreign memory (MemorySegment, MemoryAddress, and SegmentAllocator)
  2. Manipulate and access structured foreign memory (MemoryLayout, VarHandle),
  3. Manage the lifecycle of foreign resources (ResourceScope)
  4. Call foreign functions (SymbolLookup, CLinker, and NativeSymbol).

The porposal is to include the FFM API in the jdk.incubator.foreign package of the jdk.incubator.foreign module.

As an example, here is Java code snippet that obtains a method handle for a C library function radixsort and then uses it to sort four strings which start life in a Java array (a few details are elided):

// 1. Find foreign function on the C library path
CLinker linker = CLinker.getInstance();
MethodHandle radixSort = linker.downcallHandle(
                             linker.lookup("radixsort"), ...);
// 2. Allocate on-heap memory to store four strings
String[] javaStrings   = { "mouse", "cat", "dog", "car" };
// 3. Allocate off-heap memory to store four pointers
MemorySegment offHeap  = MemorySegment.allocateNative(
                             MemoryLayout.ofSequence(javaStrings.length,
                                                     ValueLayout.ADDRESS), ...);
// 4. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
    // Allocate a string off-heap, then store a pointer to it
    MemorySegment cString = implicitAllocator().allocateUtf8String(javaStrings[i]);
    offHeap.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 5. Sort the off-heap data by calling the foreign function
radixSort.invoke(offHeap, javaStrings.length, MemoryAddress.NULL, '\0');
// 6. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
    MemoryAddress cStringPtr = offHeap.getAtIndex(ValueLayout.ADDRESS, i);
    javaStrings[i] = cStringPtr.getUtf8String(0);
}
assert Arrays.equals(javaStrings, new String[] {"car", "cat", "dog", "mouse"});  // true

Pattern Matching for switch – JEP 420

This is a core language update. The proposals aims to enhance switch statements and expressions.

As an example, here is a Switch expression:

    int numLetters = 0;
    Day day = Day.WEDNESDAY;
    switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> numLetters = 6;
        case TUESDAY                -> numLetters = 7;
        case THURSDAY, SATURDAY     -> numLetters = 8;
        case WEDNESDAY              -> numLetters = 9;
        default -> throw new IllegalStateException("Invalid day: " + day);
    };
    System.out.println(numLetters);

Extending pattern matching to switch allows to test an expression against a number of patterns, each with a specific action. This way, you will be able to express complex data-oriented queries in a concise and safe way.

As an example,in the following code, the value of “o” matches the pattern Long l. Therefore, the code associated with case Long l will be executed:

Object o = 123L;
String formatted = switch (o) {
    case Integer i -> String.format("int %d", i);
    case Long l    -> String.format("long %d", l);
    case Double d  -> String.format("double %f", d);
    case String s  -> String.format("String %s", s);
    default        -> o.toString();
};