Going Beyond Java 8: Local Variable Type Inference (var)
Let's see when and how to use the most important new feature introduced with Java 10, officially called "local variable type inference" but better known as "var introduction."
Join the DZone community and get the full member experience.Join For Free
According to some surveys, such as JetBrains's great survey, Java 8 is currently the most used version of Java, despite being a 2014 release.
What you are reading is one in a series of articles titled 'Going beyond Java 8,' inspired by the contents of my book, Java for Aliens. These articles will guide you step-by-step through the most important features introduced to the language, starting from version 9. The aim is to make you aware of how important it is to move forward from Java 8, explaining the enormous advantages that the latest versions of the language offer.
In this article, we will talk about the most important new feature introduced with Java 10. Officially called local variable type inference, this feature is better known as the introduction of the word
var. Despite the complicated name, it is actually quite a simple feature to use. However, some observations need to be made before we can see the impact that the introduction of the word
var has on other pre-existing characteristics.
In recent years, an important part of the evolution of the Java language has been dedicated to making the syntax more synthetic (in programming we usually prefer to say less verbose). To do this, efforts have been made to assign additional tasks to the compiler. In the case of the type inference for local variables, the compiler is able to automatically infer the type of the local variable we are declaring, allowing us to use the word
var instead of the type of the variable. For example, suppose we have the
JavaBook class, we know how to instantiate an object with the following syntax:
Instead, using the type inference for local variables, we can write:
In fact, the compiler is able to infer the type of the
obj1 variable by reading its initialization, that is the right-hand side (RHS) of the declaration. The immediate advantage is that we can write less verbose code and make fewer mistakes without compromising on readability.
However, the use of the word
var could make our code less readable. In fact, in the previous example we used a so-called manifest type, because by reading the declaration of the variable
obj1, the inferred type is also evident to us programmers. Just read the right side of the statement instead of the left side.
The inference of the type, however, would also take place if, instead of clearly assigning an instance of the
JavaBook class to the variable
obj1, we assign it the return value of a method. For example, suppose you have the following method available in the same class:
We could, instead, use the type inference in this way:
In this case, the code is less readable for us programmers. In fact, although the compiler automatically infers the type for
obj2, we will have to read the return type in the
getInstance method declaration to discover that it is a
JavaBook type. For this reason, we say that we're using an unmanifest type. In this specific case, we've improved the verbosity of our code at the expense of readability, and that doesn't seem like a good decision. We should always favor the maintenance of our programs rather than save some typing on the keyboard.
We all know that giving meaningful names to our variables is very important in Java programming. In the case of using an unmanifest type, choosing a meaningful variable name becomes even more important. The
obj2 identifier, for example, does not seem to be suitable since it does not give us information about its type. In this particular case we could instead declare the variable as follows:
In this way, we could reasonably assume the type of the variable. It is therefore essential to use meaningful names for the variables that we want to declare through the word
Is It a Keyword?
var is not a keyword, but a reserved type name. A keyword cannot be used for any type of identifier (variables, methods, modules, classes, interfaces, etc.), whereas a reserved type name has fewer restrictions. In particular, in order not to impact too much on the pre-Java 10 code, it was decided that it is permissible to use the
var identifier for variables (both instance and local). It is even possible to declare a local variable like this:
In the above code, the first
var is the reserved type name, while the second is the variable identifier.
It is also possible to use the
var identifier to declare a method. In fact, the method
In fact, the method is syntactically correct.
var can also be used as a package identifier without any problems:
It can also function as an identifier for modules.
The only limitation that has been imposed is that it is not possible to declare a Java type (i.e. a class, an interface, an enumeration, an annotation, or a record) with the
var identifier. For example, the following statement:
Would produce the following compile-time error:
However, due to the convention that requires type names to start with a capital letter, the chances that the introduction of the word
var could cause damage to code written before the advent of Java 10 are very minimal.
You can use the word
var with all primitive and complex types. For example, the following snippet compiles without any problem:
As asserted before, type inference only works for local variables. Therefore, it is not applicable for instance variables, for return types of a method, for the type of a parameter of a method, etc.
Furthermore, the word
var cannot be used for local variables that are not initialized at declaration time, as in the following example:
Which will produce the output:
var is also not usable if the variable is initialized to
The inference cannot be used even in the case of multiple variable declarations. For example, this code:
Will produce the following compile-time error:
Finally, we cannot use
var as an identifier to declare arrays. For example, the snippet:
var varArray = new int; will cause the following error:
In fact, as regards the inference of the type for an array, the compiler infers the array type using the left-hand side of the declaration (LHS), while the word
var requires us to use the right-hand side (RHS) of the declaration.
It is also possible to use the word var as an index type in the initialization of a
for loop. For example, we can write:
In fact, the type will be inferred from the value in the right part of the assignment. In our case
0 is considered an
Or we can use the word
var in place of the data type of the temporary variable in an enhanced
for loop (better known as a foreach loop). So the following code is valid:
We note that in the previous example, the use of the word
var favors the evolution of the code. In fact, without modifying the loop, we can change the array to our liking. For example, the following are all valid declarations for
The foreach loop will work with any of these declarations without having to be changed.
Applicability: Switch Expressions
We can also use the word
var with a variable to which a switch expression is assigned, a feature preview introduced with Java 12 and made official with Java 14. With the
switch expression, we can use the old
switch construct as a poly-expression, in the sense that it can define multiple expressions (we will soon dedicate an article from the 'Going beyond Java 8' series to this construct).
When the type to be returned by the
switch expression is known, then all the
case clauses must return values consistent with the type. This means that the following snippet is valid:
In fact, the compiler checks the right-hand side (RHS) of the expression, or, more precisely, of the expressions. Since, in the three expressions, a
short and an
int are returned, obviously the
int type is inferred as the return type of the
switch expression. This is odd, since if we had used
int instead of the word
var directly, the compiler would have relied on the left-hand side of the declaration (LHS) to determine if the types returned by all
case clauses are compatible.
Applicability: Lambda Expressions and Method References
As with arrays, the compiler for lambda expressions is designed to read the type of the variable in the left part (LHS) of the declaration, while the word
var is designed to replace a data type that can be inferred by analyzing the right part (RHS) of the declaration. This means that it is not possible to use the word
var with a local lambda expression.
For example, consider the following statement:
If we try to use
var instead of the type:
We would get the following compile-time error:
Predictably, the word
var cannot be used with method references either. In fact, as with method references, the left part (LHS) of the declaration allows the compiler to infer the details of the declaration. With the use of
var, we would get, as usual, a compile-time error. For example, if we consider the following functional interface:
We can assign it as an implementation for the reference of the
System.out object (
println, which does not wrap after printing):
But if we try to use
var instead of the type
We will get the following compile-time error:
Applicability: Local Anonymous Classes
We know that an anonymous class is always declared with the purpose of overriding one or more methods of the type it extends. Now let's consider the following code which declares an anonymous class that extends the
Object class. But, instead of overriding one of its methods, it defines a new one:
In the last line, we try to call the
test method using the reference
testObject which is of type
Object, obtaining an error during compilation:
In fact, an
Object type reference cannot invoke a method defined in another class. In order to invoke this method, we would need a reference of the type of the anonymous class. But a reference of the anonymous class cannot exist, just because anonymous classes do not have a name. However, with the introduction of the word
var, this limit can be overcome!
Let's rewrite the previous class using the word
We can see that we could use both the
name variable and the
test method, which we defined from scratch in the local anonymous class. In this case, therefore, the introduction of the word
var, in addition to representing a tool to reduce the verbosity of Java, has allowed us to overcome a limitation of the language.
In this article, we have introduced the word
var, and we have seen how we can use it instead of the type of local variables. The obvious advantage of using
var is to streamline code, but it is not the only one. For example, we have seen that it supports software evolution, and that, surprisingly, it makes local anonymous classes more useful and powerful, allowing us to take advantage of new solutions. The local variables type inference is, therefore, another reason to move forward from Java 8.
Even ignoring the increased security offered by the latest versions of the JDK, there are plenty of reasons to upgrade your knowledge of Java, or at least your own Java runtime installations. My book 'Java for Aliens', which inspired the 'Going Beyond Java 8' series, contains all the information you need to learn Java from scratch, and uses a well-tested teaching method that has been perfected over 20 years of experience, which makes learning simple and exciting. It is also structured to deepen the topics and provide superior knowledge that can make a difference in your career.
This article is inspired by various paragraphs of chapters 3, 4, 11, and 17 of the book 'Java for Aliens.' Actually, the topic is explored further in other chapters where the impact of this feature is evaluated, for example, on polymorphism.
For more information visit https://www.javaforaliens.com.
Published at DZone with permission of Claudio De Sio Cesari. See the original article here.
Opinions expressed by DZone contributors are their own.