dcsimg

Java SE 9 Features and Code Examples

In this part of our ongoing series discussing the history and features of Java, we look at the Java SE 9 core release, some of its most important added features, and some example code demonstrating their use.

Java SE 9 was released on September 21, 2017 and introduced a slew of new features and add-ons. In fact, there were so many that we could not include all of them in this article. Instead, we have chosen to highlight the most important ones. For a more complete list, you can visit the What’s New in JDK 9 page at Oracle.

Java SE 9 - Most Important Changes

As Java Users, the important changes that impacts our daily development lives fall in the areas of:

The Java language itself Core Libraries

As stated, there are a plethora of other changes, however, those are best explored on an as-needed basis. Before we move forward looking at the new features, we will need to refresh our memory little and revisit one of the changes in Java SE 8 - default interface methods.

Before we begin that review, however, feel free to take a moment to read the rest of the articles in this series on Java history if you have not already done so or wish to read them again:

Revisiting Java SE 8 Default Interface Methods

One of the best features added to Java SE 8 was default interface methods. These methods were especially useful for Enterprise development or any piece of software where you needed to easily achieve backward compatibility while, at the same time, allowing new method addition or the update of API contracts. Often in existing or legacy code that had interfaces that require new methods, any code that inherits from the interfaces will breaks existing code. If we added new methods, we had to provide the implementation for each of these newly added methods in the implementing classes. This created many maintenance issues, even though - per the basic principles of SOLID and OOP programming - It is always recommended that you provide an interface without any implementation. This is the definition of an interface in Java. But to handle scenarios for backward compatibility while, at the same time, introducing newer Interface Methods, we needed to handle and solve the problem above. This is where Default Interface Methods come in to play. Here is an example in code:

import java.util.List;  

interface LegacyBusinessAPI {  

    /**  
    * Additional Default Method that Can be Invoked on Each Type of Invoice that Implements the LegacyBusinessAPI.   
    * It can be over-ridden by the Implementing Invoice Types. This is an Example Usage/Benefit of the Java/JDK 8  
    * Default Interface Feature.  
    *
    * @param items  
    */  
    default void checkStatusOnEachInvoice (List invoiceItems) {  
    String invoiceItem = null;
        for(int i=0; i<invoiceItems.size(); i++) {
            invoiceItem = invoiceItems.get(i);   

            // null check is not done - always a value exists!
            if(invoiceItem.startsWith("OLD_")) {  
                invoiceItems.remove(invoiceItem);  
            }  
        }  
        return;  
    }  

} 

In the above example, the LegacyBusinessAPI is an interface in a legacy or existing business application. It is already being extended by multiple invoice types. Per changing business requirements, re-engineering efforts requires that each of the invoices have a method to remove or auto-expire an item marked "old". Prior to Java 8, Such a problem would require the introduction of a new method declaration in the interface. It would have then required that each of the implementing classes implement their own logic for handling this, which equates to a lot of effort, time, code changes and is error prone.

After the introduction of default interfaces, this became much simpler. The benefits of interface default methods include:

  • No breakage of existing functionality when using default methods.
  • Implementing Classes can choose to override default method.
  • Abstract Classes can be used to group similar implementations.

Java Language Updates in Java SE 9

@SafeVarargs Allowed on Private Instance Methods

Non-Reifiable variable argument parameters used in a method can cause multiple warnings when trying to compile code.

@SafeVarargs can be used to suppress unchecked or unsafe operations warnings. Using this annotation, developers can notify the compiler that they have ensured they will not be causing heap pollution (for example, unsafe forEach operations).

Prior to Java 9 @SafeVarargs were allowed on non-overridable methods. This included use in constructors, static methods, and final instance methods. Note, however, that the annotation will throw an error if it is used in fixed arity or non-Varargs methods. Starting with Java 9, @SafeVarargs could be safely used on private instance methods.

Beginning in Java 9, using underscores (_) in a variable name was no longer allowed. Prior to this, underscores were already marked as a reserved keyword; using them in variable names will result in compilation failure.

When working with legacy code, be aware that code may need to be rewritten to address this.

Another change presented in Java 9 was the introduction of private interface methods. Private methods allow code sharing between Non-abstract methods in the interface. The rules related to ordinary private modifiers are applicable to these methods. Note that a method cannot be both private and abstract. Also, a private interface method must have a method body.

Prior to Java 9, using generics and diamond operators was not allowed with anonymous classes. This was primarily because compilers could not infer whether they could represent the type in the argument that gets passed to the diamond operator.

JSR 334 had the following to say about this issue:

"Using diamond with anonymous inner classes is not supported since doing so in general would require extensions to the class file signature attribute to represent non-denotable types, a de facto JVM Change."

"Internally, a Java compiler operates over a richer set of types than those that can be written down explicitly in a Java program. The compiler-internal types which cannot be written in a Java program are called non-denotable types. Non-denotable types can occur as the result of the inference used by diamond. Therefore, using diamond with anonymous inner classes is not supported since doing so in general would require extensions to the class file signature attribute to represent non-denotable types, a de facto JVM change. It is feasible that future platform versions could allow use of diamond when creating an anonymous inner class as long as the inferred type was denotable."

After the introduction of Java 9, the Java compiler changed its inference algorithm. Now, diamond operators can work alongside anonymous classes with the caveat that argument types are denotable.

Core Library Updates in Java SE 9

The following are library updates introduced in Java 9.

JEP 102: Process API Updates

Using ProcessHandle you can retrieve the PID of a process via a native call. Information about the currently running Java process or JVM can also be obtained. In addition, it became possible to return a snapshot of every process currently running on the system.

JEP 193: Variable Handles

Java's Concurrent Package (java.util.concurrent.atomic) provided atomic types for performing atomic operations. Aside from this, unsafe operations (for example, creating objects without calling the constructor) used in low-level Java programming had to be hidden, per JEP 260: Encapsulate Most Internal APIs.

This led to the creation of a new abstract class type called VarHandle, which allowed developers to assign different types to the same reference. It also took care of performing atomic operations on held variables, including compare and swap operations. Memory fencing operations to order the in-memory representation of the object were also added.

JEP 254: Compact Strings

Beginning in Java 9, String storage has been modified, to begin with, an additional encoding flag. The flag is used to indicate whether there are ISO-8859-1/Latin-1 characters or UTF-16 characters.

JEP 264: Platform Logging API and Service

This defines a minimal logging API for the platform and also provides a Service Interface for the consumers of these messages. The implementation for this interface can be provided by logging libraries or the application itself to route the messagesappropriately to the application-specific logging framework or implementation being used, such as [Log4J or SLF4J]. Whenever an implementation is not available, the run-time switches to Default Java Logging Package. (java.logging). This also allows us to detect Bootstrap issues.

JEP 266: Concurrency Updates

Starting with Java 9, the interoperable publish-subscribe framework was added. So, to, were enhancements to the CompletableFuture object, alongside numerous implementation improvements over JDK 8, which includes Javadoc rewording.

JEP 268: XML Catalogs

XML catalogs are made of entries from one or more catalog entry files. A Catalog entry file is an XML file whose document element is Catalog and whose content follows the XML Catalog DTD defined by OASIS. Beginning in Java 9, a public API for XML Catalog management was added. The external references in the Catalog needed to be resolved repetitively. But with this new API, an API containing CatalogManager, Catalog, and CatalogResolver can be used to reduce or eliminate external invocations, specifically when the catalog resource already exists locally. A Local Catalog gets created and allows JAXP-based processors to resolve to this local Catalog.

JEP 269: Convenience Factory Methods for Collections

The addition of factory method collections makes it convenient for developers to create Immutable Collections out of existing collections, including a Set, Map, or Lists. A static factory method of() added to each of the interfaces — Set, Map and List. It is important to understand the following:

They are Structurally Immutable. They Disallow Null elements or null keys. They are Serializable if all elements are serializable. They Reject Duplicate Elements/Keys at creation time. The Iteration Order of set elements is Unspecified and is Subject to Change. They are Value-Based. Factories are free to create new instances or reuse existing ones. Therefore, Identity-Sensitive Operations on these instances (Reference Equality (==), Identity Hash Code, and Synchronization) are Unreliable and should be avoided. They are serialized as specified on the Serialized Form page.

JEP 274: Enhanced Method Handles

A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values. These transformations are quite general and include such patterns as conversion, insertion, deletion, and substitution.

JEP 277: Enhanced Deprecation

With an eye on maintainable and more informative code, developer-defined deprecation now allows us to mark deprecation with additional elements of information like forRemoval and since. The forRemoval allows us to mark that this item may be removed in future versions of Java and since it provides information about when it was first introduced.

JEP 290: Filter Incoming Serialization Data

This feature is related to the addition of filters at the serialization of incoming streams to improve security and robustness. The core mechanism is a filter interface implemented by serialization clients and set on an ObjectInputStream. The filter interface methods are called during the deserialization process to validate the classes being deserialized, the sizes of arrays being created, and metrics describing stream length, stream depth, and the number of references as the stream is being decoded. The filter returns a status to accept, reject, or leave the status undecided.

JEP 259: Stack-Walking API

Before Java 9, accessing Stack Trace was very limited and returned the entire dump or stack information all at once. This was inefficient and did not allow any direct way of filtering data. With Java 9, a Lazy StackWalker API was introduced, allowing developers to fetch data based on filter conditions.