W3docs

Java var Keyword (Local-Variable Type Inference)

Use var for local variable type inference in Java, when it improves readability, and when it doesn't.

Java var Keyword (Local-Variable Type Inference)

Since Java 10, you can declare a local variable with var and let the compiler infer its type from the initializer. var greeting = "hello"; is exactly the same, compiled bytecode and all, as String greeting = "hello"; — the type is still String, you simply didn't type it twice. This is local-variable type inference: a syntactic convenience that trims redundant type names without making Java dynamically typed. Used well it removes noise; used carelessly it hides the very information a reader needs.

var is inference, not dynamic typing

The single most important fact: var is not a new "anything" type. The compiler reads the right-hand side, works out the static type, and bakes it in. From that point on the variable is as strongly typed as if you had written the type by hand — you cannot reassign it to an unrelated type, and the inferred type is fixed at compile time.

var name = "Ada";   // name has static type String, forever
name = "Lovelace";  // fine, still a String
name = 42;          // compile error: int cannot be assigned to String

var is reserved type name, not a keyword — you can still use var as a variable or method name (though you shouldn't). It only triggers inference in the local-variable declaration position.

Where var is allowed — and where it isn't

var works only for local variables that have an initializer. The compiler needs a right-hand side to read the type from; without one there is nothing to infer.

Positionvar allowed?Reason
Local variable with initializerYesThe initializer supplies the type
Index/element in for loopsYesThe loop expression supplies the type
Try-with-resources variableYesThe resource expression supplies the type
Local variable without initializerNoNothing to infer from
Fields / instance variablesNoInference is local-only by design
Method parametersNoCallers, not initializers, supply values
Method return typesNoSame reason as parameters
Initialized to null onlyNonull has no concrete type
Lambda parameters (bare)Special(var x, var y) -> ... is allowed since Java 11
var x;                       // error: cannot infer type, no initializer
var nothing = null;          // error: null has no type to infer
public var field = 1;        // error: var not allowed on fields
void m(var p) { }            // error: var not allowed on parameters

The real payoff: collapsing noisy generics

var earns its keep when the type name is long, repeated, or generic-heavy. The classic case is a declaration where the type appears in full on both sides of the =:

// Before: the type name is written twice
Map<String, List<Customer>> byCity = new HashMap<String, List<Customer>>();

// After: the right side already says everything
var byCity = new HashMap<String, List<Customer>>();

It also shines with iterators, Map.Entry, and other verbose types that add no clarity when spelled out:

for (var entry : byCity.entrySet()) {     // Map.Entry<String, List<Customer>>
  System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}

When NOT to use var

var helps when the type is obvious from the right-hand side and hurts when it isn't. If a reader has to run the code in their head to know the type, write the type out.

var result = service.process(input);   // unclear: what does process return?
Order result = service.process(input); // clear: an Order

var flag = true;                       // fine, obviously boolean
var count = list.size();               // fine, obviously int

Watch the numeric literal trap: var infers the literal's type, not the type you may have meant.

var n = 100;        // int, not long  — for a long you must write 100L or long n
var f = 3.14;       // double, not float
byte b = 1;         // explicit type narrows; var b = 1 would be int

Avoid var when it loses a deliberate interface type. var list = new ArrayList<String>(); types list as ArrayList<String>, not List<String> — fine locally, but if you meant to program to the interface, say so.

A worked example you can run

This program exercises var across all its legal positions — simple values, a generic map, an enhanced for-loop, an indexed for-loop — and uses getClass().getSimpleName() to prove the inferred runtime types are exactly what the right-hand sides implied.

java— editable, runs on the server

What to take from the run:

  • greeting.getClass().getSimpleName() prints String, proving var greeting = "hello" produced a genuine Stringvar is compile-time inference, and at runtime the object is exactly what the literal implied, nothing dynamic about it.
  • count + 1 = 43 and price * 2 = 19.98 confirm the numeric inference rules: 42 made count an int, 9.99 made price a double. The literal's type — not your intent — decides, which is the trap to remember when you need a long or float.
  • scores type = HashMap shows var captured the concrete right-hand-side type HashMap, not the Map interface; the diamond <String, List<Integer>> on the right gave the compiler everything it needed even though the left side said only var.
  • total chars = 10 comes from for (var name : names) where name was inferred as String, so name.length() resolved correctly (3 + 3 + 4) — var works in enhanced for-loops, inferring the element type from the iterable.
  • 0..4 sum = 10 comes from for (var i = 0; ...) where i was inferred as int from the literal 0; the indexed loop is one of the cleanest places to use var because the type is unmistakable.

Practice

Practice

In which of these declarations is 'var' legal and does it infer the type the comment claims?