The Java version 10 brought a new reserved word (var) and with it a new mechanism to the language: the Local-Variable Type Inference.

Source code

The code that was used to create this article is versioned in Java10Examples project on github. Fork or clone to execute in your computer. Please, star this project and follow me on github.

The old way

Sure that every developer declares local variables and follows the Java common syntax to do it:

(...)
    type variableName = variableConstruct
(...)

Everybody knows that it’s needed to tell java what kind of variable developers are working, so type is the variable type that should be told to Java.

The variableName should adhere to Java guidelines on how to name a variable in Java.

And the variableConstruct must tell Java how to get a variable of that type. As examples:

    int x = 10;
    String name = "Java";
    LocalDate now = LocalDate.now();

In Java 10, the type inference must address to these declarations and simplify programmers life.

Type inference

By definition, type inference is the ability of a programming language, at compile time, to deduce, partially or totally, the type of an expression when not occurring explicit declaration of the type of a variable.

Translating this definition into code:

(...)
    var x = new Object();
(...)

The compile should deduce that the x variable is an instance of the java.lang.Object class.

Implementing Languages

Currently, many programming languages implement this feature (some of them only in more current versions). There’s a little list of them:

  • C++11
  • C#
  • F#
  • JavasScript
  • OCaml
  • Swift
  • Kotlin
  • Haskell
  • Scala
  • Go
  • Rust
  • and others…

With Java 10, Java enters on this list!

JEP 286

JEP are enhancement proposals to the Java programming language. On the main site, there is a list of this proposals. One of them is the JEP 286 and its main goal is:

Enhance the Java Language to extend type inference to declarations of local variables with initializers.

And with this, a new reserved word has been included: var.

E desde então, uma nova palavra reservada foi incluída no rol do Java: var.

Basic Example

As a basic example, the following code shows how to use type inference with primitive types:

var b = false;
var y = (byte)10;
var c = 'A';
var s = (short)100;
var i = 0;
var l = 999l;
var f = 3.14f;
var d = 1.61803398874d;

Object example

Another basic example is about declaring objects based upon Java types. The following code shows how to do this and get some essential information regarding the declared objects:

var hello = "Hello! This is an inferred String!";

var now = LocalDate.now();

var formatter = DateTimeFormatter.ISO_DATE;

var stringList = new ArrayList<String>();

stringList.add("All");
stringList.add("Java");
stringList.add("developers");
stringList.add("have");
stringList.add("type");
stringList.add("inference");
stringList.add("now");
stringList.add("!");

System.out.println("Printing a String: ");
System.out.println(hello);
System.out.println("\nPrinting a formatted LocalDate: ");
System.out.println(now.format(formatter));
System.out.println("\nPrinting a String List: ");
System.out.println(stringList);

System.out.println("\nPrinting the inferred classes: ");
System.out.println(hello.getClass());
System.out.println(now.getClass());
System.out.println(formatter.getClass());
System.out.println(stringList.getClass());

The execution result is:

Printing a String:
Hello! This is an inferred String!

Printing a formatted LocalDate:
2018-05-03

Printing a String List:
[All, Java, developers, have, type, inference, now, !]

Printing the inferred classes:
class java.lang.String
class java.time.LocalDate
class java.time.format.DateTimeFormatter
class java.util.ArrayList

for e for-each

It’s possible to use the type inference inside a traditional for loop and it’s evolution, the for-each, as seen on the following code:

System.out.println("\nListing with simple for");
for(var i = 0; i < stringList.size(); i++) {
    System.out.println(stringList.get(i));
}

System.out.println("\nListing with for-each");
for(var string : stringList) {
    System.out.println(string);
}

That will result in:

Listing with simple for
All
Java
developers
have
type
inference
now
!

Listing with for-each
All
Java
developers
have
type
inference
now
!

try-with-resources

Also, it’s possible to use the type inference inside the try-with-resources mechanism. The following code shows how:

System.out.println("\nReading a file with try-with-resources");
try(var fileReader = new FileReader("README.md")) {
    var buffer = new char[1024];

    while(fileReader.read(buffer) > 0) {
        System.out.println(new String(buffer));
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

It’ll result on the listing of the README.md file, referred on the FileReader class constructor.

A little more complex

Not only standard Java classes are allowed to use this new feature, but other foreigners classes could use that. The following code creates a shopping list, put it in a bill and print the result on the screen:

var bill = Bill.newBill();

var productLaptop = new Product("Good Laptop", 1000f, 10f);
var productGamerMouse = new Product("Gamer Mouse", 20f, 1);
var productGamerKeyboard = new Product("Gamer Keyboard",15, 1);
var productUSBController = new Product("USB Controller", 35, 2);

bill
    .addProduct(productLaptop, DiscoutType.COMMON_DISCOUNT, 0.15f)
    .addProduct(productGamerMouse, DiscoutType.INTERNET_DISCOUNT, 0f)
    .addProduct(productGamerKeyboard, DiscoutType.NO_DISCOUNT, 0f)
    .addProduct(productUSBController, DiscoutType.COMMON_DISCOUNT, 0.20f);

var productList = bill.productList();

System.out.println("-[ Bill ]-----------------------------------------------------------");

for(var product : productList) {
    System.out.println(product.toString());
}

System.out.println("--------------------------------------------------------------------");

System.out.println(String.format("%44.2f", bill.total()));
System.out.println(String.format("%68.2f", bill.taxes()));

System.out.println("--------------------------------------------------------------------");

The result is:

-[ Bill ]-----------------------------------------------------------
Good Laptop                          1000,00                   10,00
Gamer Mouse                            20,00                    1,00
Gamer Keyboard                         15,00                    1,00
USB Controller                         35,00                    2,00
--------------------------------------------------------------------
                                      926,00
                                                               14,00
--------------------------------------------------------------------

The following picture illustrate what’s happenning:

beans

Other uses

The local variable type inference goes beyond. It’s possible to do other things that in older java versions are improbable and impossible.

Adding a member to a concrete class

It’s possible, using the type inference, define new members to concrete classes. With this, it’s possible to add new behavior where there was not before.

The following code shows two examples. The first adds a method in the Product class called applyInternetPrice() where calculates a discount for products bought on the internet. The second part of the code just shows that the original class hasn’t been modified, so that’s not possible to access the new created method for the previously created object.

public class ProductExample {

    public static void main(String[] args) {
        var modifiedProduct = new Product("An internet product", 8f, 0.5f) {
            public float applyInternetPrice() {
                return getPrice() - 1f + getTax();
            }
        };

        System.out.println("Modified product");
        System.out.println(modifiedProduct.calculateTotal());
        System.out.println(modifiedProduct.applyDiscount(0.15f));
        System.out.println(modifiedProduct.applyInternetPrice());
        System.out.println(modifiedProduct.getClass());
        System.out.println("----------------");

        var commonProduct = new Product("A not internet product", 8f, 0.5f);

        System.out.println("Common product");
        System.out.println(commonProduct.calculateTotal());
        System.out.println(commonProduct.applyDiscount(0.15f));
        System.out.println(commonProduct.getClass());
        System.out.println("--------------");
    }

}

The result of execution is:

Modified product
8.5
7.3
7.5
class org.java10.examples.ProductExample$1
----------------
Common product
8.5
7.3
class org.java10.examples.beans.Product
--------------

It must be noted that a new class (org.java10.examples.ProductExample$1) was created using the type inference.

Access Modifiers

Java has four accessors modifiers:

  • public
  • protected
  • private
  • default access or package private

With type inference the same access rules are applied. It was created an entity located in the org.java10.examples.access package, with the following implementation:

package org.java10.examples.access;

public class Accessors {

    public String publicAccessor = "public";
    private String privateAccessor = "private";
    protected String protectedAccessor = "protected";
    String defaultAccessor = "default";

}

Using the previous feature (add an unimplemented method), the following code shows a class located in a superior package (org.java10.examples) trying to access to the defined variables on the entity. However, it’s only possible to access the public and protected members as expected.

package org.java10.examples;

import org.java10.examples.access.Accessors;

public class AccessorsExample {

    public static void main(String[] args) {
        var accessorTest = new Accessors() {
            public void accessorResult() {
                System.out.println(String.format("Accessing publicAccessor member: %s", publicAccessor));
                System.out.println(String.format("Accessing protectedAccessor member: %s", protectedAccessor));
            }
        };

        accessorTest.accessorResult();
    }

}

The execution result is:

Accessing publicAccessor member: public
Accessing protectedAccessor member: protected

Another class was implemented the same way as the previous, however, it’s located in the same package as the entity. This time the package private member has been accessed.

package org.java10.examples.access;

public class AccessorsExample2 {

    public static void main(String[] args) {
        var accessorTest = new Accessors() {
            public void accessorResult() {
                System.out.println(String.format("Accessing publicAccessor member: %s", publicAccessor));
                System.out.println(String.format("Accessing protectedAccessor member: %s", protectedAccessor));
                System.out.println(String.format("Accessing defaultAccessor member: %s", defaultAccessor));
            }
        };

        accessorTest.accessorResult();
    }

}

This execution result is:

Accessing publicAccessor member: public
Accessing protectedAccessor member: protected
Accessing defaultAccessor member: default

In sum, private members are never accessed. To access package private members, the redefined class should be located in the same package.

Mixins and Traits

Mixin is a class that includes methods of other classes without an is-a relationship (inheritance). In Java the default methods in interfaces implement the concept of Traits. Now it’s time to implement a Mixin.

These classes were create to create a Mixin:

  • Vehicle, a super interface representing all kind Vehicles
  • Navigable, an interface to marine vehicles
  • Flyer, an interface to air vehicles

It’s desirable to create a Seaplane, a marine, and an air vehicle. A Mixin must be created for that:

public class MixinExample {

    public static void main(String[] args) {
        var seaplane = (Navigable & Flyer) Vehicle::create;

        System.out.println("------------------------------------");
        System.out.println("Start!");
        System.out.println("------------------------------------");

        seaplane.takeOff();
        seaplane.fly();
        seaplane.land();
        seaplane.starboard();
        seaplane.port();
        seaplane.anchorDown();
        seaplane.anchorUp();
        seaplane.sail();

        System.out.println("\n------------------------------------");
        System.out.println("What class is this?");
        System.out.println("------------------------------------");

        System.out.println(seaplane.getClass());

        System.out.println("\n------------------------------------");
        System.out.println("Instance of ...");
        System.out.println("------------------------------------");

        System.out.println("seaplane instanceof Vehicle = " + (seaplane instanceof Vehicle));
        System.out.println("seaplane instanceof Flyer = " + (seaplane instanceof Flyer));
        System.out.println("seaplane instanceof Navigable = " + (seaplane instanceof Navigable));

    }
}

The class diagram is:

mixin

The execution result is:

------------------------------------
Start!
------------------------------------
Taking off! Up, up and away!
Flying!
Tripulation, prepare for landing!
Starboard!
Port!
Anchor is down! Let's take some rest!
The anchor is up! Ready to sail!
Sailing!

------------------------------------
What class is this?
------------------------------------
class org.java10.examples.MixinExample$$Lambda$1/2096171631

------------------------------------
Instance of ...
------------------------------------
seaplane instanceof Vehicle = true
seaplane instanceof Flyer = true
seaplane instanceof Navigable = true

What you can’t do with Java type inference

JEP 286 is pretty amazing, but there are some uses that are not allowed and the developers should avoid. These are declarations:

  • Cannot use ‘val’ on variable without initializer:
var x;
  • Lambda expression needs an explicit target-type:
var f = () -> { };
  • Variable initializer is ‘null’:
var x = null;
  • Inferred type is non-denotable:
var c = l();
  • Method reference needs an explicit target-type:
var m = this::l;
  • Array initializer needs an explicit target-type:
var k = { 1 , 2 };

Also, it’s available an implementation guideline to use the local variable type inference.

Conclusion and Thoughts

This new Java feature, introduced on the version 10, allows to remove the remaining verbosity, so alleged by the other languages, but it also allows to create members in concrete classes and create mixins.

In another post, I will investigate the compatibility among other Java versions such as 7, 8 and 9. It’ll behave as expected or will it fail to use the type inference?

Sources

One thought on “JEP 286 – Java 10 Local-Variable Type Inference – The var reserved word

Leave a comment