Java 10 and Local-Variable Type Inference

Robert Rice Java, Technology Snapshot Leave a Comment

JDK 10, an implementation of Java Standard Edition, was released in March 2018. It brought with it Local-Variable Type Inference to help simplify the writing of Java applications.

Basically, it’s a new syntax meant to reduce some of Java’s verbosity, while still maintaining the enforcement of static type safety. In simpler terms, you are able to declare variables, but won’t necessarily have to specify the type.

In this blog, I give recommendations for best practice when using Local-Variable Type Inference in JDK 10 with an eye for common var pitfalls.

Type Inference

Though not a new concept in Java, type inference is a new concept for Java local variables. This is accomplished with the addition of a var reserved type name. Note that var is not a keyword, so any code that already uses var as the identifier of some member of package won’t be affected.

For example, lines like these:

List list = new ArrayList();
Stream stream = getStream();

They are replaced with this:

var list = new ArrayList();
var stream = getStream();

The road to Local-Variable Type Inference was actually paved in Java 5 with the introduction of Generics. Later, in Java 7, the diamond operator was introduced (<>), which allows the developer to initialize lists without binding a type to them, in this way…

List list = new LinkedList<>();

The well-known wordiness of Java can be helpful when trying to understand the intention of a written method (either your own or someone else’s). But using var can help to alleviate some of the tediousness of Java’s verbosity. It is redundant to declare the type on the variable if this can easily be inferred from the initializer.

Usage

There are a few legal uses of var that might cause some confusion. The first is when assigning the return of a method call. For example:

var x = doMethod();

The type returned from the method is not easily ascertained in this situation. But, that is why we have IDEs (and the help they provide) and don’t write code in notepad. Well, someone may write code in notepad, but if that’s the case, they have bigger problems than type inference. 🙂

See Also:  Four Common Mistakes That Make Automated Testing More Difficult

Another potentially-problematic legal usage is with the diamond operator…

var x = new HashMap<>();

Though very concise, it is not clear which parameters are instantiated as part of this type.

Restrictions

So, when can we and when can we not use var? The usage is restricted to:

  1. Local initialized variables
  2. Indexes used in enhanced for-loops
  3. Local variable declared in traditional for-loops

Var cannot be used in:

  1. Method parameters
  2. Constructor parameters
  3. Method return types
  4. Fields
  5. Catch formals

A few other restrictions:

  • Cannot instantiate multiple variables (var x=0,y=0;)
  • Cannot instantiate with null values, whether explicitly or as a return from a method call
  • Cannot reassign to a different type, so doesn’t play well with polymorphism

Readability

Finally, a recommendation about when to use var when both its usage and non-usage are perfectly legal. Although type inference does reduce the verbosity of Java, it can potentially negatively affect readability.

Since developers spend a significantly greater amount of time reading source code than writing it, it may be beneficial to use var with a mind on ease-of-reading rather than ease-of-writing.

Using explicit types can sometimes hinder readability.

For example, when looping over the entries in a Map, you need to include the type parameters on the Map.Entry object. Take this example of looping over a Map from a parent, to the names of each of its children:

Map<String, List> parentToChild = new HashMap();
// ...
for (Map.Entry <String, List>childrenOfParent : parentToChild.entrySet()) {
  List children = childrenOfParent.getValue();
  // ...
}

Rewriting this with var would reduce the repetition and boilerplate:

var parentToChild = new HashMap<String, <List>>();
// ...
for (var childrenOfParent : parentToChild.entrySet()) {
  var children = childrenOfParent.getValue();
  // ...
}

Readability is not the only advantage gained; maintaining code also gains an advantage. For example, if we take that same code that uses explicit types and replace the String representing the name of the child with a Child domain class (that could contain additional information about the child) then we need to rewrite all the code that is relying on that specific type being exposed. For example:

Map<String, List<Child>> parentToChild = new HashMap();
// ...
for (Map.Entry<String, List<Child>> childrenOfParent : parentToChild.entrySet()) {
  List<Child> children = childrenOfParent.getValue();
  // ...
}

If we had used var and type inference, then we would need to only alter the first line of code. For example:

var parentToChildren = new HashMap<String, List<Child>>();
// ...
for (var childrenOfParent : parentToChildren.entrySet()) {
  var children = childrenOfParent.getValue();
  // ...
}

And this illustrates a key principle with the use of var: don’t optimize for ease-of-writing, or ease-of-reading, rather optimize for ease-of-maintenance.

See Also:  White Paper Published: Blockchain for the Enterprise

This should balance the benefit of readability with the balance of maintenance. It would be foolish to claim that adding type inference is always a positive for your code – sometimes having explicit types in code can help readability. This is particularly the case when the type isn’t obvious from the expression that generates it.

In the following two snippets I would use the explicit over the implicit as I don’t know from just reading the getChildren() method call what it is returning.

Map<String, List<Child>> parentToChildren = getChildren();
var parentToChildren = getChildren();

This does bring up one final recommendation when it comes to readability and the usage of var: variable names matter! Because var removes the ability for the reader of your code to guess at its intent simply from the type of the variable, it puts more of a burden on providing good names for local variables. In theory that’s something that we Java developers should already be putting effort into.

Concluding

In practice, a lot of readability problems in Java code don’t relate to new language features. Instead, they relate to existing practices such variable naming that we don’t always get right.

What Do You Think?