The “var” syntax for local variables since Java 10
Java 10, released on March 2018, has introduced the new “var” syntax. This small but helpful feature allows you to declare local variables using the word “var” instead of specifying the variable type. This article is focused on describing all the essential rules about this new feature clearly and concisely.
Introduction
The new “var” syntax brings a new level of type inference into the Java language because the compiler can now infer the variable type from the initialized value. So, in practice, it’s possible to do something like:
var message = "Hello"; // Java 10+
var bignum = BigInteger.valueOf(1234); // Java 10+
instead of:
String message = "Hello";
BigInteger bignum = BigInteger.valueOf(1234);
There are some well-known rules and restrictions on this new syntax. Some rules are easy and obvious to understand, while others are less intuitive. However, all these rules are here to stay, so you must learn and remember them as described in the following sections.
- The word “var” is not a keyword
- Can only be used for “local” variables
- Must always be explicitly initialized with a value
- Can have the final modifier and/or annotations
- Cannot be initialized with a literal null
- Cannot define more variables in the same declaration statement
- Cannot use square brackets [] next to the variable name
- Cannot initialize arrays using the short { } syntax
- Cannot be initialized with a lambda expression
- The word “var” can no longer be used as a type name
The word “var” is not a keyword
The Java Language Specification (JLS for short, available on the Java SE Specifications page) is quite clear about this point. Section §3.9. Keywords of the JLS clearly state that “var” is not a keyword, and thus it is not a reserved word of the language.
Instead, “var” is a contextual keyword with special meaning only in the context of local variable declarations. This has some particular and surprising consequences because “var” can still be used as a regular identifier in other variable declarations. For example, it is perfectly legal to do this:
public static int square(int var) { // Ok return var * var; }
and also this:
BigInteger var = BigInteger.valueOf(1234); // Ok
And it is even possible the following (but please, never do this!):
var var = "Hello"; // Yes, it's legal!! System.out.println(var);
Can only be used for “local” variables
The primary and most important restriction of the new “var” syntax is that it can be used only on local variables. Thus, you can use it for the following:
- local variables in any method, constructor, and instance/static initialization block
- the variable declared in the “classic”
for
cycle and also in the enhanced-for cycle (the so-called “for-each”) available since Java 5 - variables declared in the try-with-resource statement, available since Java 7
However, the new syntax cannot be used for the following:
- parameters of methods/constructors
- the declared return type of a method
- “instance” (non-
static
) and “class” (static
) variables - the parameter of the
catch
clause
Must always be explicitly initialized with a value
The variable declared with the new “var” syntax must always be initialized at the same time as the declaration. This is perhaps the most important rule of this new feature. This requirement is because the compiler needs to infer the type from the initialized value. In other words, the initialization cannot be done in a second separate step.
// ❌ NO, ERROR!
var num;
num = 123;
var num2;
num2 = Math.random();
// ✔️ OK
var num = 123; // Ok, num is inferred of type int
var num2 = Math.random(); // Ok, num2 is inferred of type double
For the same reason, it makes no sense to initialize the variable assigning the result of a void
method since there is no returned value.
// ❌ NO, ERROR!
var res = System.out.println("hello"); // println is void !
Note: remember that void
is not a data type, not even a reference type! (and not to be confused with java.lang.Void
)
Can have the final modifier and/or annotations
A local variable declared using the new “var” syntax can have the final
modifier (which makes it a “constant”) and/or one or more annotations. This works exactly like any other “classic” local variable declaration.
final var num = 123.456; // Ok, num is a constant of type double
@SomeAnnotation var str = "Hello"; // Ok with an annotation
Note: annotations on local variables are very, very rarely used!
Cannot be initialized with a literal null
The null reference, represented in source code by the literal null
, is a value of the special null type that denotes the absence of reference to an object. Furthermore, the null reference is technically the subtype of all the other reference types. For these reasons, you cannot use the new “var” syntax to initialize a variable with a literal null
because the compiler cannot deduce a type in this context.
// ❌ NO, ERROR!
var str = null; // What type???
If you really want to initialize the variable with a null
value, you may help the compiler by inserting an explicit cast, like the following:
var str = (String) null; // Ok, but ... pointless
This would give the compiler sufficient information to deduce the variable type. However, I think this is a bit pointless, and in my opinion, it’s better to use the “classic” variable declaration ( String str = null;
), which is also shorter.
Cannot define more variables in the same declaration statement
The “classic” way of declaring variables allows you to define (and possibly initialize) more than one variable in the same declaration statement, as shown below:
int a = 123, b = 789; // Ok, always legal
Unfortunately, the new “var” syntax prohibits this type of multi-variable declaration. Instead, you can define only one variable in a single declaration statement.
// ❌ NO, ERROR!
var a = 123, b = 789;
// ✔️ OK
var a = 123; // Ok
var b = 789; // Ok
Cannot use square brackets [] next to the variable name
The “classic” way of declaring array variables requires placing square brackets []
next to the variable name for each array dimension. The square brackets are generally and conventionally placed just on the right of the type, like in the example below:
int[] arr = new int[10];
double[][] matrix = new double[4][8];
With the new “var” syntax, placing the brackets []
next to the variable name is illegal because the compiler automatically infers the array type from the assigned value.
// ❌ NO, ERROR!
var arr[] = new int[10];
var[] arr = new int[10];
var matrix[][] = new double[4][8];
// ✔️ OK
var arr = new int[10]; // Ok, arr is inferred of type int[]
var matrix = new double[4][8]; // Ok, matrix is inferred of type double[][]
Cannot initialize arrays using the short { } syntax
The “classic” way of declaring array variables allows you to initialize the array using the short form with curly brackets { }
to enclose the values, like in the example below:
int[] numbers = { 10, 20 }; // Ok, always legal
String[] strings = { "A", "B" }; // Ok, always legal
With the new “var” syntax, it is illegal to initialize the array using this short form because the compiler cannot infer the array type only from the values (it would not be possible in all cases, to be precise). Indeed, there could be some particular or corner cases in which it is hard/impossible to deduce the correct or expected array type.
You must use an anonymous array if you really want to initialize the array using the new “var” syntax. This particular form of array instantiation uses the keyword new
followed by the array type and then the values enclosed in curly brackets.
// ❌ NO, ERROR!
var numbers = { 10, 20 };
var strings = { "A", "B" };
// ✔️ OK
var numbers = new int[] { 10, 20 }; // Ok with anonymous array
var strings = new String[] { "A", "B" }; // Ok with anonymous array
Cannot be initialized with a lambda expression
Lambda expressions are a great feature introduced in Java 8 to define “functions” more compactly and expressively. A lambda expression must always be associated with a functional interface (a conceptually particular type of Java interface
) because the function’s signature depends on the functional interface’s Single Abstract Method (SAM).
For example:
IntBinaryOperator intAdder = (a, b) -> a + b;
In the above example, the compiler infers that the function takes two ints
and returns an int
because IntBinaryOperator
(the type of the intAdder
variable) has the single “functional” method:
int applyAsInt(int left, int right)
This type inference is the opposite of the new “var” syntax because, with var
, the compiler must infer the type from the assigned value. For this reason, it is not possible to directly assign a lambda expression to a variable declared with the “var” syntax.
// ❌ NO, ERROR!
var intAdder = (a, b) -> a + b; // What type is the function???
If you really wanted to assign a lambda expression to the variable in the above code, you must help the compiler by inserting an explicit cast, like the following:
// ✔️ OK
var intAdder = (IntBinaryOperator) (a, b) -> a + b;
In this latter case, the compiler has sufficient information to infer the appropriate variable type.
The word “var” can no longer be used as a type name
At first, this restriction may seem a little strange, but it’s easy to understand. Since “var” can now be used in place of a type, it is no more possible to define a new type (e.g., a class
or interface
) using exactly the name “var”.
// ❌ NO, ERROR!
public class var { }
// ❌ NO, ERROR!
public interface var { }
Anyway, this restriction is absolutely the least important and the least problematic for a straightforward reason: declaring a type with the exact name “var” is a clear violation of the standard naming conventions.
A set of naming conventions is widely recognized and accepted by the Java developers community. According to these conventions, Java type names should begin with an uppercase letter (e.g., Person, Book, CardDeck). Therefore, if you follow and respect these conventions, you should never use the name var
for a Java type.
Conclusions
In this article, I have explained the rules of the new “var” syntax in a complete but concise way. However, if you want more detailed and extensive information, you can read the JEP 286, the JDK Enhancement Proposal explicitly written for this new feature.