Is it that time again?

Java 10 is finally here! A closer look at the new features

Falk Sippach

Oracle delivers the new Java 10 just in time with the previously announced deadline. Those who expected big and groundbreaking changes will be disappointed, but there are still a lot of great new features. Falk Sippach, trainer, software developer and project manager at OIO Orientation at Objects GmbH, took a good look at those and will go here into further detail.

Java 10 is final – Is it that time again?

Imagine if the latest version of Java was released on time and on schedule according to the official release date. It’ll never happen, you say? Well, apparently it just did. We even have proof! (And more here.) It does seem a bit awkward, because it feels like Java 9 was only just released. But, just as it was announced back in early September 2017, the next version has been released on its planned deadline.

The development of new Java versions was, up until now, very feature driven. This meant that you had to wait for a few years for the next release. Major changes like Generics, Lambdas, Streams, and Jigsaw made this fact bearable, but quite a lot of the smaller and very useful language improvements were consistently delayed this way. Oracle has now switched to a new, time based model – a move that is still being discussed and viewed as controversial.

We as Java developers can now look forward to a new version every six months. But apparently, not everyone agrees with this proceeding and the Java Community is split on the issue. Larger companies also appreciated the stability and the low rate of change of Java so far.

Oracle has responded to these concerns and continues to offer long-term releases on a regular basis, but also at longer intervals. And after Java 8, it is Java 11, which will probably receive a long term support again.

Java 9 and Java 10 on the other hand will only be supported for the time period of half a year, until the next release is due. In fact, Java 9’s support has just ended, since Java 10 is out. You can find more information on this on Oracle Support Roadmap.

Honestly, one reason why this hasn’t been too bad is because we all had barely acclimated to Java 9 yet. Quite a lot of tool and framework manufacturers are still working on adapting to the new module system. For instance, JUnit has just released version 5.1, which supports testing in Jigsaw modules. Even the world of Java Enterprise (Java EE, EE4J, Jakarta EE and MicroProfile) seems to be unsure how modularization is supposed to find its way into application servers and servlets containers.

Java 10 – The new features

But let’s get back to Java 10. Thanks to the short time frame you don’t have to struggle through too long release notes. The list on the OpenJDK project page is quite clearly laid out:

List of features in Java 10

List of Java 10 features

From a developer’s point of view, the Java Enhancement Process (JEP) 286 for the Local Variable Type Inference is most interesting feature on this list. Type Inference is the conclusion on data types from the remaining specifications of the code and the typing rules. This prevents an unnecessary inflation of the source code and saves time on the writing work, which increases the readability in return. We already know this fairly well, since it mimics the same structure from Java and its Lambda parameters and with the Diamond operator for generic data containers respectively for anonymous, inner classes.

// Type inference bei Parametern von Lambda Ausdrücken (Java 8)
Function<String, String> helloFunction = s -> "Hello " + s;
// Inference of generics (Diamond Operator, since Java 7)
List strings = new ArrayList<>();
// Inference of generics (Diamond Operator) with anonymous inner classes (Java 9 -> JEP 213)
Consumer printer = new Consumer<>() {
    public void accept(String string) {

With the keyword var, local variables can now be defined very concisely and their data type results directly from the assignment of the value. While the Diamond operator concludes the Type information from the left side of the assignment, the Local-Variable Type Inference is exactly the other way around.

// int
var zahl = 5;
// String
var string = "Hello World";
// BigDecimal
var objekt = BigDecimal.ONE;

Once declared var, variables are set to the assigned data type. If you want to assign values of other types later, you will get compiler errors due to the failed type conversion.

// Type is set by the compiler at declaration and initial allocations
var zahl = 5;
zahl = 7L; // Incompatible types: possible lossy conversion from long to int
var objekt = BigDecimal.ONE;
objekt = BigInteger.TEN; // Incompatible types: BigInteger cannot be converted to BigDecimal

This means that a value must always be assigned during declaration. Otherwise, compiler errors will also occur even if a value is guaranteed to be assigned shortly afterwards, as in the following example.

// Declaration of "var" only at direct initialization of the variable
var flag = true;
var number; // Compiler error
if (flag) {
    number = 5;
} else {
    number = 7;

The type inference also works with generic types and within the foreach loop. However, the combination with the Diamond operator leads to a container of object references, due to the missing type information and thus leads to less type security.

// Inference at reallocation of a value (var does not imply "final")
var zahl = 5;
zahl = 7;
// Inference is also usable with "final", otherwise "effectively final semantic" applies since Java 8 
final var zahl = 5;

Variables declared with var, as the name already indicates, are not automatically unchangeable. You can combine them with the keyword final. They are also effectively final (if there is only one assignment) and can therefore also be used from inner classes and lambda expressions without explicitly declaring them as final.

// Inference of generic types (List<String>)
var strings = Arrays.asList("World", "Java 10");
// Inference in Loops
for (var string : strings) {
    System.out.println("Hello " + string);
// In combination with the Diamond Operator this leads to the inference of List<Object>
var strings = new ArrayList<>();
strings.add("Hello World");
for (var string : strings) {
    System.out.println(string.replace("World", "Java 10")); // cannot find symbol 'replace'
var strings2 = new ArrayList<String>();
strings2.add("Hello World");
for (var string : strings2) {
    System.out.println(string.replace("World", "Java 10"));

The type inference uses the concrete type, what means that for anonymous internal classes, two instances derived from the same interface may not be assigned to the same var variable.

// Inference uses exact typing
var runnable = new Runnable() {
    public void run() {
// incompatible types: <anonymous Runnable> cannot be converted to <anonymous Runnable>
runnable = new Runnable() {
    public void run() {

However, this also means that newly implemented methods can be called without compiler errors for local types (anonymous inner class implementations) (reverseMe()).

// Inference of local types
var myReversibleStringList = new ArrayList<String>() {
    List<String> reverseMe() {
        var reversed = new ArrayList<String>(this);
        return reversed;

Work is still ongoing for Java 11, specifically on an extension of the local variable Type Inference, which works in lambda expressions. This is necessary for combining type inferences with type annotations.

// A look at Java 11 (JEP 323)
/ Inference of parameters in Lambda expressions
Consumer<String> printer = (var s) -> System.out.println(s); // statt s -> System.out.println(s);
// But no mixture of "var" and declarated types possible
// BiConsumer<String, String> printer = (var s1, String s2) -> System.out.println(s1 + " " + s2);
// Useful for type annotations
BiConsumer<String, String> printer = (@Nonnull var s1, @Nullable var s2) -> System.out.println(s1 + (s2 == null ? "" : " " + s2));

The remainder of the release notes are concerned with the infrastructural area and the operation of Java applications. The G1, which has been the standard garbage collector since Java 9, can parallelize the full garbage collection and thus shorten the stop-the-world cycles. The memory footprint can be reduced by sharing loaded classes between multiple Java applications.

Furthermore, there is a new, still-experimental JIT (Just-In-Time) Compiler (Graal) and improvements to the JVM for working with Docker Containers. The OpenJDK Trust Store is now delivered with a certain amount of root certificates, which was previously only available for Oracle Java SE versions.

The class-library (JDK) has also gone through some small changes as well. This includes an overloaded version of orElseThrow() in the Optional class and various factory methods for creating unmodifiable collections and stream collectors. Further changes can be found in the release notes or via the JDK-API-Diff tool.

A not so exciting release

In conclusion, Java 10 is not exactly a cause for alarm.  It’s impressive enough that Java 10 was delivered as-promised and Just-in-Time like its compiler. Will the Java community accept this new delivery model? Only time will tell. But now developers can now have new, useful language features on a regular basis and that is definitely a good thing.


Have fun with Java 10 and let us know what you think. And don’t forget, Java 11 is scheduled for September.


Falk Sippach

Falk Sippach has over 20 years of experience with Java and works as trainer, software developer and project manager at the OIO Orientation in Objects GmbH in Mannheim, Germany.

Inline Feedbacks
View all comments
Ala eddine
Ala eddine
3 years ago

I see java will become like javascript and node js technolgy in its statements and expressions .

3 years ago

Thanks for the post, I have added it to, please share the link with your developer communities.

3 years ago

I don’t think var is a good idea. Its usage is limited. And it introduces confusing. You have to read the other codes to know the variable type rather than where it is declared.