This week we’re going to look at local variable type inference. The goal of this was to extend the type inference mechanism to local variable declarations with initializers. This was a preview language feature in JDK 10, and is now permanent.
Local variable type inference introduces a var
reserved type name. We can use var
instead of specifying an exact data type. The var
type name is used on the left side of an assignment statement. The compiler automatically infers the correct data type from the right side of the assignment.
Simple Example of Local Variable Type Inference
Using the var
type name means that instead of doing the following inside a method:
String name = "Rumpelstiltskin";
We can just type:
var name = "Rumpelstiltskin";
The compiler infers that name
is a String
and is equivalent to the previous version, but just with less typing.
This does not break static typing. The name
variable is a String
, and as such we will not be allowed to assign an int
to it:
name = 1234; // compiler error!
More Examples of Local Variable Type Inference
We’re already aware of type inference as far as generic types are concerned. Instead of specifying types on both sides of the assignment operator, we can use the type inference (diamond) operator and omit the second type declaration:
// infers ArrayList<String> ArrayList<String> list = new ArrayList<>();
We often create local variables as “throw-away” variables – variables used as a temporary step towards creating another variable or result. It would be nice to reduce extra coding (and thought) by removing possibly unnecessary local variable type declarations.
We are familiar with code similar to the following:
// list is a throw-away variable ArrayList<String> list = new ArrayList<>(); Stream<String> stream = list.stream();
Local variable type inference allows us to create simpler code as follows:
// list is inferred as type ArrayList<String> var list = new ArrayList<String>(); // stream is inferred as type Stream<String> var stream = list.stream(); // sum is inferred as type int var sum = stream.mapToInt(String::length).sum(); // extra code... list.add("Hello, world"!); Consumer<String> reader = s -> { // data is inferred as type String[] var data = s.split("\\s+"); // use data appropriately... }
Here’s another example of a chained / throw-away variable:
var path = Paths.get(fileName); var bytes = Files.readAllBytes(path);
Usage Restrictions
The identifier var
is a reserved type name, and not a keyword. Any Java code that uses var
as an identifier for variables, method names or package names will compile. Code that uses var
as an interface or class name must be changed (but probably should have been changed anyway because those names violate the standard naming conventions).
We can use var
when we create local variable declarations with initializers. We can also use var
for index variables in both normal for
loops and enhanced for-each loops.
We cannot use var
for fields, constructor/method formal parameters, method return types, catch formal parameters, or any other kind of variable declaration.
Local variable declarations that do not have initializers, or that declare multiple variables, or have extra array dimension brackets, or reference the variable being initialized are not allowed.
If the initializer has the null
type, a compiler error occurs. Like a variable without an initializer, this variable is probably intended to be initialized later, and the compiler doesn’t know what type we will use later.
For more information about var
, see JEP 286: Local-Variable Type Inference.
As always, please share your comments and questions