Declaring Variables with Explicit Types in Java
A variable declared with var in Java 11 and later has a definite type, taken from its initial value by the compiler.
A Prediction Before the Explanation
Java 11 introduced the var keyword, which lets you declare a local variable without writing its type name. Many learners who meet var for the first time assume it changes Java into a language where one variable can hold a number at one moment and a piece of text at another. That assumption is natural, because languages such as Python let a variable hold whatever value is assigned to it. This article tests that assumption directly, and then explains what the compiler actually does.
What do you think happens?
Suppose you write var rate = 0.05; and then later write rate = "high";. Does this compile in Java 11 or later?
Reveal answer
No, because rate has a fixed type that was set by its initial value
A var declaration gives the variable a definite type taken from the initial value. Here that value is 0.05, so rate is a decimal type, and assigning a String to it is a compile-time error. The keyword changes how the declaration is written, not what kind of variable is created.
What the Compiler Reads
A var declaration gives the variable a type, and that type comes from the value on the right side of the equals sign. The compiler looks at the initial value, determines what type that value has, and assigns that type to the variable. Nothing about the variable is left open for a later decision. The diagram below traces this flow for a single declaration.
The important word is fixed. The type is not a loose label that can drift over time. It is determined when the declaration is compiled. The value stored in the variable can change later, as long as the new value is compatible with the type. The type itself does not change.
Why Reassignment Is Checked Against the Fixed Type
Because the type is fixed at compile time, every later assignment to the variable is checked against that type. An assignment that fits the type compiles. An assignment that does not fit is rejected before the program runs. The code below uses the rate variable from the prediction to show both outcomes.
- var rate = 0.05 fixes rate as a decimal type taken from 0.05
- rate = 0.10 compiles because the new value has the same type
- rate = 3 compiles because the whole number 3 can be stored in a decimal variable
- rate = "high" is commented out because it would not compile
error: incompatible types: String cannot be converted to double
(the message produced by javac when the commented line is uncommented)The rule to apply when predicting a reassignment is simple. Ask whether the new value can be stored in the type the compiler already fixed. If it can, the line compiles. If it cannot, the line fails, even though the variable is declared with var and not with an explicit type name.
Anatomy of a var Declaration
A var declaration has the same parts as any other local variable declaration, except that the type name is replaced by the keyword var. It has the keyword, a name, an equals sign, and an initial value. The annotated strip below labels each part.
Where var Is Allowed and Where It Is Not
The var form has two firm restrictions. It can only be used for local variables, meaning variables declared inside subroutines, and a variable declared with var must be given an initial value. The table compares the positions discussed in the source material.
| Position | Is var allowed? | Reason |
|---|---|---|
| Local variable inside a method | Yes | A local variable with an initial value gives the compiler a value to infer from |
| Loop control variable in a for loop | Yes | The loop control variable is a local variable, and its declaration follows the same form |
| Local variable with no initial value | No | The compiler has no value from which to determine the type |
| Field of a class | No | The var form is restricted to local variables |
| Method parameter | No | The var form is restricted to local variables |
Positions where var may and may not be used, based on the restrictions in the source material
var in a for Loop
The loop control variable of a for loop is a local variable, so var can be used there. The declaration part of the loop takes the same form as any other var declaration: the keyword, a name, and an initial value, with the type inferred from that value. The rest of the loop header works as before. The two loops below behave the same way, and the second one only removes the explicit type name.
- The first loop declares its counter with the explicit type int
- The second loop declares its counter with var, and the initial value 0 fixes the type as int
- Both counters are fixed in the same way, so the counter cannot be assigned an incompatible type inside either loop
The source notes that for simple values such as a loop counter, the explicit type is short and clear, so var adds little. Its value increases for parameterized types, which appear later in the course. There the type names can be long and complicated, and var keeps the declaration readable while the compiler still checks the type.
Mistakes Learners Make with var
Common mistake Believing var makes the variable dynamically typed
What it looks likevar rate = 0.05; rate = "high";Why it failsThe type is fixed when the declaration is compiled. The keyword changes how the declaration is written, not the kind of variable created.
Do this insteadTreat the type of a var variable as if the type name had been written. Check later assignments against the type taken from the initial value.
Common mistake Omitting the initial value
What it looks likevar total;Why it failsThe compiler determines the type from the initial value, and a declaration without one gives it nothing to work from.
Do this insteadSupply an initial value, such as var total = 0;, so the type can be determined.
Common mistake Using var for a field or a method parameter
What it looks likeA class field declared as var count = 0;Why it failsThe var form is restricted to local variables, meaning variables declared inside subroutines.
Do this insteadUse an explicit type name for fields and method parameters.
Common mistake Assuming a later assignment is accepted because the value looks similar to the original
Initializers That Leave the Type Undetermined
The compiler can only fix a type when the initial value has one it can read. Some initializers do not give it that information. A bare null, a bare array literal in braces, and a lambda expression all leave the compiler without a type it can take from the value, so a var declaration using one of them is rejected. The explicit type name is the way to declare these variables. This follows from the rule that the type comes from the initial value: when that value does not establish a type, there is nothing for var to use.
- The bare array literal in the first line does not give the compiler a type to infer, so it is rejected
- The explicit declaration on the second line names the array type and compiles
Checking Your Understanding
Given the declaration var count = 10;, decide whether each of these later statements compiles: count = 25; then count = 2.5; then count = "ten";. For each one, state the type the compiler fixed and explain your answer using the rule about where the type comes from.
Hints
- Start by identifying the type that the initial value 10 gives the variable
- Ask whether each new value can be stored in that fixed type, and remember that a whole number can be stored in a decimal variable but a decimal cannot be stored in an integer variable
- Text cannot be stored in a numeric variable at all
Key Takeaways
- A variable declared with var in Java 11 and later has a definite type, taken from its initial value by the compiler at compile time.
- var changes how a declaration is written, not what kind of variable is created. The type does not change, so var does not make Java dynamically typed.
- Each later assignment is checked against the fixed type. Values compatible with that type compile, and incompatible values are rejected before the program runs.
- var is allowed only for local variables, including the loop control variable of a for loop, and every var declaration must include an initial value.
- Initializers such as null, a bare array literal, or a lambda do not establish a type, so an explicit type name is needed for those declarations.