Local Variable Type inference in Java 10

Habeeb Okunade
4 min readMar 13, 2020

--

Local variables are the most essential tool in Java. They let methods to compute significant results by reasonably storing provisional values. Unlike a field or instance variable, a local variable is declared, initialized, and used in the same block it was declared. The name and initializer of a local variable are often more significant to the developers than its type. Usually, the name and initializer bring just as much information as the type. Let’s look at the example below:

Shape s = new Shape();

Looking at the right-hand side of the equal sign only, we will immediately conclude that s is of type Shape. This code is pretty simple and straight forward; the type Shape has only 5 characters. The problem becomes visible when we have to declare local variables type that looks like the following:

Map<Integer, List<Shape>> valMap = new HashMap<Integer, List<String>>();

Imagine we have to write a few more variables with a type that is similar to the above code, then we will realise what goodies the JavaScript, Scala, Go, Kotlin developers were blessed with.

Java 10 var keyword to the rescue!

Local variable type inference was introduced in Java 10. Though type inference was improved a lot in Java 8 with the introduction of lambda expressions, method references, and Streams, local variables still needed to be declared with proper types — but that’s now gone.

The role of var in a local variable declaration is to represent the type so that its type can be inferred from its initialization:

var s = new Shape();

This feature is built under JEP 286: Local-Variable Type Inference, and authored by none other than Brian Goetz, author of Java Concurrency in Practice, one of the most popular books for Java developers

Using var can make code brief without sacrificing readability, and in some cases, it can improve readability by removing redundancy. This does not make Java dynamically typed like in JavaScript. With var keyword, Java is still a statically typed language only that the compiler will infer the type of the variable at compilation time, using type obtained from the variable’s initializer. The inferred type is then used as the type of the variable. Naturally, this is the same as the type you would have written explicitly, so a variable declared with var works exactly as if we had written the type plainly.

Where can we use var keyword

var can be used for declaring local variables, including index variables of for-loops and resource variables of the try-with-resources statement but cannot be used for fields, method parameters, and method return types because types in these locations appear explicitly in class files and in Javadoc specifications. With type inference, it’s quite easy for a change to an initializer to cause the variable’s inferred type to change.

var can only be used when there is an initializer i.e. initializer is required on the right-hand side of the assignment. We could have chosen to infer the type from the assignments to the variable, but that would have made the feature considerably complex, and it could potentially lead to misleading or hard-to-diagnose errors. In order to keep things simple, var is defined so that only local information is used for type inference. The following code will throw a compiler error at compilation time:

var age;
....
age = 24;

var cannot be used where the right-hand side is null or returns null because the null literal denotes a value of a special null type (JLS 4.1) that is the subtype of all reference types in Java. The only value of the null type is null itself.

Examples of using var

var str = "Java 10"; // infers String

str infers type String

var stream = list.stream(); // infers Stream<String>

stream infers type Stream<String>

var list = new ArrayList<String>(); // infers ArrayList<String>

list infers type ArrayList<String>

var list = List.of(1, 2.0, "3");

list will be inferred into List<? extends Serializable & Comparable<..>>

var bos = new ByteArrayOutputStream();

The above code is equivalent to ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream repeats twice. One of it has been eliminated by using the var feature of Java 10.

We can do similar things while using try-with-resource statements in Java. Consider the following code:

try (Stream<Student> data = dbconn.executeQuery(sql)) {
return data.map(...)
.filter(...)
.findAny();
}

which is equivalent to

try (var data = dbconn.executeQuery(query)) {
return data.map(...)
.filter(...)
.findAny();
}

Can also be used in a loop

for(var s : arrList){
....
}
for(var i=0; i<str.length(); i++){
var ch = str.getCharAt(i);
}

That’s some of the examples of using keyword var in Java 10, an interesting Java 10 feature that allows us to declare local variables without declaring their type.

--

--

Habeeb Okunade

Innovative software development professional. Loves sharing knowledge and learning latest stacks. Expertise in microservices architecture using spring boot.