Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Local Variable Type Inference in Java

DZone's Guide to

Local Variable Type Inference in Java

Local variable type inference was a long-awaited addition to Java. Let's see what JDK 10 brought to the table and how to make the most of LVTI.

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

This article is featured in the new DZone Guide to Java: Features, Improvements, & Updates. Get your free copy for more insightful articles, industry statistics, and more!

Variable type inference — the ability of the compiler to automatically deduce the type of a variable — has become an essential part of most strongly typed programming languages. While many languages such as C++, Scala, Kotlin, and C# have introduced rich type inference mechanics over the years, Java has been noticeably absent in this department.

Prior to JDK 10, a vast majority of the type inference capability  of Java was focused on the right-hand side (RHS) of expressions and was largely seen in lambda argument type inference, the diamond operator, and generic method argument type inference:

listOfStudents.removeIf(s -> s.getId() == desiredId);
List<Integer> ints = new ArrayList<>();
public <T> Bar foo(List<T> elements) { /* … */ }
Bar bar = foo(new ArrayList<Integer>());


Although these features eliminated a great deal of development tedium, Java has been severely lacking left-hand side (LHS) type inference support — namely, variable type inference.

With the adoption of JDK Enhancement Proposal (JEP) 286 in JDK 10, Java now includes the var reserved type name which allows for the LHS type (known as the manifest type) of an initialized local variable to be inferred by the Java compiler:

var i = 0;
var students = new ArrayList<Student>();
var iter = list.listIterator();


This may seem to be a trivial improvement in the language, but its true usefulness becomes evident when comparing the following declarations:

Iterator<List<SomeLongBeanName>> iterator = someList.iterator();
var iterator = someList.iterator();


The ability of the compiler to deduce the type of a variable removes much of the clutter in code that is otherwise overly verbose. It is important to note that var is reserved type name and not a keyword. This means that var can still be used as a variable name, method name, or package name, but cannot be used as a class or interface name. Thus, the following are all valid uses of var:

var var = 0;
public void var(int var) { /* … */ }
package var;


Since var is treated as a reserved type name, classes and interfaces can no longer be named var since the compiler would be unable to distinguish between a variable of type var or the use of type inference. This creates a backwards incompatibility with existing JDK releases, but the use of var as a class or interface name is counter to long-held Java conventions and therefore, it is unlikely that such a class or interface is currently in use.

Where Can Var Be Used?

Although some languages allow for very powerful type inference schemes, the Java team decided to reduce the scope and applicability of the var reserved type to a few specific contexts:

  • Local variables with initializers

  • Indices in the enhanced for-loop

  • Locals declared in traditional for-loops

Therefore, all of the following are valid uses of var as a reserved type name:

var foo = 1;
for (var student: students) { /* … */ }
for (var i = 0; i < 10; i++) { /* … */ }


This means that var cannot be used as the type of method parameters, constructor parameters, method return types, catch parameters in try-catch statements, fields, or any other type of variable declaration. Although this may appear restrictive, the three valid contexts enumerated above cover a large number of practical cases without requiring complex type inference mechanics.

In many cases, a developer expects the compiler to read his or her mind in order to infer the correct type of a variable or parameter. For example, what is the correct type of a variable initialized to null? Or a lambda expression that accepts a single argument and returns a value of the same type? What's more, how should type inference be handled if a variable is not initialized at declaration and assigned a value at a later time, possibly in a distant portion of code?

Being that developers must have a solid understanding of how types are inferred (in order to be comfortable allowing the compiler to infer types) and type inference is no trivial matter for compilers, the Java team settled on a very simple implementation of type inference: A single reserved type name var that handles only local variables and for-loop locals.

Why Var?

It is important to note that a great deal of research was performed in selecting var as the only type inference mechanism thus far. Many languages make a distinction between immutable variable initialization (i.e. final int x = 0) and mutable variable initialization (i.e. int x = 0) and use different type inference qualifiers for each. For example, Scala and Kotlin both use var and val to denote mutable and immutable variable initialization, respectively.

While differing type names do provide an important visual distinction between these initializations, it also disrupts the uniformity of variable declarations:

var mutableX = …
var mutableY = …
val immutableZ = …
var mutableA = …


In order to resolve this fiercely subjective issue, a survey was released by the JEP 286 team in 2016 that inquired of developers which scheme was most desirable. It found that 24% of respondents preferred var only, 48% preferred the combination of var and val, and 14% preferred the combination of var and let (like in Swift). Although the var-val pairing appeared to win the contest, the survey also found that 80% of respondents were accepting of var only and 19% hated var only, 74% accepted and 26% hated the var-val pairing, and 59% accepted and 41% hated the var-let pairing. Thus, the Java team decided on the most acceptable and least hated solution: var only.

Image title

While 74% of survey respondents favored the inclusion of local variable type inference (12% more were accepting and only 10% thought it was a bad idea), one of the most ardent, albeit justifiable, complaints of variable type inference in Java is reduced readability.

What About Readability?

One of the most repeated complaints local variable type inference in Java is its uncanny ability to make code less readable. One of the most used examples of this drawback is the following:

var x = y.getFoo();


While there is some merit to this complaint, it is not completely justified. As pointed out by the Brian Goetz, this lack of readability is derived primarily from the lackluster naming of the variable x, not from its hidden type. This example demonstrates that readability has much more to do with variable naming than the explicit typing of variables.

Therefore, with proper variable naming, var can actually improve readability in some very important ways, as seen in the following snippet:

Map<Integer, Student> idToStudentMap = epository.getStudentIdMap();
List<Student> enrolledStudents = repository.netEnrolledStudents();
Address addressOfTopStudent = topStudent.getAddress();
var idToStudentMap = repository.getStudentIdMap();
var enrolledStudents = repository.getEnrolledStudents();
var addressOfTopStudent = topStudent.getAddress();


Apart from removing visual clutter, the use of var also improves the focus on the variable names in variable declarations.

As with all features of a language, var should be used with care. Using var indiscriminately will likely reduce the readability of code, but there are many cases in which the opposite is true: Using var diligently will likely improve the readability of code. For example:

for (Iterator<String> it = list.iterator();
it.hasNext(); ) {
    System.out.println(it.next());
}
for (var it = list.iterator(); it.hasNext(); ) {
    System.out.println(it.next());
}


Conclusion

Java has been notoriously lagging in its type inference support, but the release of JDK 10 has finally ushered in local variable type inference in the form of var. While var is noticeably simpler than its counterpart in other languages, with proper diligence, it can improve the conciseness of code while reducing the tedium of explicit typing. Paired with future improvements to lambda type inference (JEP 323) and enum types (JEP 301), the addition of var is a key part in moving Java from an infamously verbose language to a reputably terse language.

This article is featured in the new DZone Guide to Java: Features, Improvements, & Updates. Get your free copy for more insightful articles, industry statistics, and more!

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:
java ,java 10 ,local variable type inference ,code readability ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}