Java™ developers are accustomed to waiting years for a new Java release, but the new, high frequency release cadence changes all of that. It’s been just six short months since Java 9 emerged, and now Java 10 is already knocking at the door. In six more months we’ll be welcoming Java 11. Some developers might find such a rapid releases overkill, but the new cadence marks a long-needed change.

True to its number, Java 10 offers 10 new features, and this article offers the five that I find most significant (you can view them all on the Open JDK 10 project page).

Get the code.

1. Java’s new release cadence

Historically, the JDK release cadence has been driven by big, new features. As recent examples, Java 8 introduced functional programming in the form of lambdas and streams, and Java 9 introduced the modular Java system. Each new version was hotly anticipated, but minor fixes were often left on the shelf, waiting for larger components to be finalized. Java’s evolution lagged behind other languages.

The new high-frequency cadence pushes Java forward in smaller increments. Features that are ready by the release date will be included, and those that aren’t can be scheduled for the next release, just six months away. The first Java version under this new cycle was Java 9, which came out in October 2017. Java 10 was released in March 2018, and Java 11 is anticipated in September 2018.

As part of the new cadence, Oracle has said that it will only support each major release until the next major one comes out. When Java 11 is released, Oracle will stop supporting Java 10. Developers wanting to ensure their Java version is supported will have to migrate to a major release every 6 months. Developers who don’t want or need such frequent migrations can utilize the LTS (long-term support) release, which is updated every three years. The current LTS release, Java 8, will be supported until Java 11 comes out this fall.

2. Local-variable type inference

Local-variable type inference is by far the most visible feature in Java 10. Strongly debated before finding its way into JDK 10, this feature allows the compiler to infer the type of a local variable, rather than requiring that the programmer explicitly specify it.

Listing 1 shows how a String variable type would be defined before Java 10.

Listing 1. Declare and assign a String variable

String name = "Alex";

Listing 2 shows the same String variable defined in Java 10.

Listing 2. A String variable defined using local-variable type inference

var name = "Alex";

As you can see, the only difference is the use of the var reserved type name. Using the expression to the right, the compiler can infer the type of the variable name as String.

That seems a bit easy, so let’s look at a more complex example. What if the variable was assigned to the return value of a method call? In this case it’s possible for the compiler to infer the variable type from the return type of a method, as shown in Listing 3.

Listing 3. A String variable inferred from return type

var name = getName();

String getName(){
  return "Alex";

Using local variable types

As the name suggests, the local-variable type inference feature is only available to local variables. It can’t be used to define instance or class variables, nor can it be used in method parameters or return types. However, you can use var in classic and enhanced for-loops where the type can be inferred from the iterator, as shown in Listing 4.

Listing 4. Use var in loops

for(var book : books){}
for(var i = 0; i (less-than 10; i++){}

The most obvious reason to use this type is to reduce verbosity in your code. Take a look at the example in Listing 5.

Listing 5. Long type names make long code

String message = "Incy wincy spider...";
StringReader reader = newStringReader(message)
StreamTokenizer tokenizer = new StreamTokenizer(reader);

Note what happens when we re-write Listing 5 using the var reserved type name.

Listing 6. The var type reduces code verbosity

var message = "Incy wincy spider...";
var reader = new StringReader(message);
var tokenizer = new StreamTokenizer(reader);

The type declarations in Listing 6 line up vertically and the type is mentioned once in each statement, to the right of the constructor call. Imagine the benefits of using this type for the long class names common in some Java frameworks.

Issues with local variable types

1. var obscures type

You’ve seen how var can improve code readability, but on the flipside it can also obscure it. Take a look at the example in Listing 7.

Listing 7. The return type is unclear

var result = searchService.retrieveTopResult();

In Listing 7, we have to guess at the return type. Code that makes the reader guess what is happening is harder to maintain.

2. var doesn’t mix with lambdas

Type inference does not work well when used with lambdas, mainly due to the lack of type information available to the compiler. The lambda expression in Listing 8 will not compile.

Listing 8. Insufficient type information

Function(less-thanString, String(greater-than quotify = m ‑(greater-than "'" + message + "'";
var quotify = m ‑(greater-than "'" + message + "'";

In Listing 8 there isn’t enough type information in the right-hand expression for the compiler to infer the variable type. Lambda statements must always declare an explicit type.

3. var doesn’t mix with the diamond operator

Type inference also doesn’t work well when used with the diamond operator. Take a look at the example in Listing 9.

Listing 9. Using the diamond operator with var

var books = new ArrayList(less-than(greater-than();

In Listing 9, what is the parameter type of the ArrayList referenced by books? You might know that you want the ArrayList to store a list of books, but the compiler can’t infer that. Instead, the compiler will do the only thing it can do, which is to infer an ArrayList parameterized by the Object type: ArrayList<Object>().

The alternative is to specify the type in the diamond operator in the right-hand expression. You can then let the compiler infer the variable type from that, as shown in Listing 10. Otherwise, you must explicitly declare the variable in the traditional way: List<Book> books. In truth, you might prefer this option because it allows you to specify an abstract type and program to the List interface:

Listing 10. Specify the type

var books = new ArrayList<Book>();

3. Additions, removals, and deprecations


Java 10 removes a number of tools:

  • The command-line tool javah, but you can use javac -h instead.
  • The command-line option -X:prof, though you can use the jmap tool to access profiling information.
  • The policytool.

Some APIs that have been marked as deprecated since Java 1.2 have also been permanently removed. These include the java.lang.SecurityManager.inCheck field and the following methods:

  • java.lang.SecurityManager.classDepth(java.lang.String)
  • java.lang.SecurityManager.classLoaderDepth()
  • java.lang.SecurityManager.currentClassLoader()
  • java.lang.SecurityManager.currentLoadedClass()
  • java.lang.SecurityManager.getInCheck()
  • java.lang.SecurityManager.inClass(java.lang.String)
  • java.lang.SecurityManager.inClassLoader()
  • java.lang.Runtime.getLocalizedInputStream(
  • java.lang.Runtime.getLocalizedOutputStream(


JDK 10 also deprecates some APIs. The package has been marked as deprecated, and so have various related classes (Certificate, Identity, IdentityScope, Singer, auth.Policy) in the package. Also, the CREDENTIAL_TYPES in the class is marked deprecated. The finalize() methods in and have been marked as deprecated. So has the finalize() method in the classes.

Additions and inclusions

As part of the ongoing alignment of the Oracle JDK and Open JDK, Open JDK now includes a subset of the root certificates authority that are available in the Oracle JDK. These include Java Flight Recorder and Java Mission Control. Additionally JDK 10 has added enhanced support for Unicode extensions of BCP 47 language tags where appropriate in the java.text, java.time, and java.util packages. Another new feature allows the execution of a callback on threads without performing a global VM safepoint. This makes it both feasible and inexpensive to halt individual threads, rather than requiring that you halt all threads or none.

4. Improved container awareness

If you deploy to containers like Docker then this feature is especially for you. The JVM is now aware that it is running in a container, and will query the container for the number of processors available for use, rather than querying the host operating system. It’s also possible to externally attach to a Java process running in a container, which makes monitoring JVM processes easier.

Previously, the JVM was unaware of its container and would ask the host operating system for the number of active CPUs. In some cases this led to over-reporting resources to the JVM, causing problems when multiple containers were running on the same operating system. In Java 10, you can configure a container to use a subset of the host operating system’s CPUs, and the JVM will be able to determine the number of CPUs in use. You also can explicitly specify the number of processors the containerized JVM is able to see, using the -XX:ActiveProcessorCount flag.

5. Application class data sharing

The objective of this feature is to improve JVM startup times between runs and with multiple JVMs running the same code, while also reducing the memory footprint. This is achieved through sharing metadata about classes among JVMs. The first run of the JVM collects and files data about the classes it has loaded. It then makes the data file available to other JVMs and subsequent runs of that JVM, saving time and resources in the JVM initialization process. Class data sharing has actually been available for some time, but restricted to system classes only. Now this feature has been extended to include all application classes.


The headline feature in Java 10 is clearly the new var reserved type name. It has the power to clarify and simplify code, though if used carelessly it can also obscure meaning and intent. An IDE may be able to help identify types when it is not clear, but not all code is read in an IDE. Often we read code online in a GitHub repository, in a debugger, or in code-review tools. Developers using this new feature are advised to be mindful of code readability for future readers and maintainers.

Java’s new, high frequency release cadence is a welcome change. It forces the release of features that are ready at the release date, and gives delayed features a shorter turnaround to the next release. The new cycle will hasten Java’s progress, and developers won’t have to wait years for features already developed and living on the shelf. There is some reasonable concern about the shorter support lifespan from one major release to the next, but the LTS should help buffer that issue. Another risk is release fatigue, as developers tire of constant migrations. Still, overall, I think this is a positive move that will help keep Java alive and evolving for some time to come.