Local Variable Type Inference (var)
Reviewed & published by Brayan K
var lets you declare a local variable without writing its type — the compiler infers one fixed static type from the value on the right, keeping your code concise while staying fully type-safe.
Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
Before You Start
You should be comfortable with data types and variables and have seen generic collections like List<String>. var needs Java 10 or newer (Java 11+ for lambda parameters).
What You'll Learn
- What var does — compile-time type inference, not dynamic typing
- Exactly where var is allowed and where it is forbidden
- Using var with generics, the diamond, and long types
- var in classic and enhanced for loops
- The readability trade-offs and when to skip var
- Why var x = new ArrayList<>(); is a trap
The Big Idea — A Label You Don't Have to Write
💡 Analogy: Imagine moving house. When a box is clearly a kettle, scrawling "KETTLE" on the side is just noise — anyone can see what it holds. var is letting the box speak for itself: var kettle = new Kettle(); still is a kettle box (the type is fixed), you just didn't bother writing the label twice. But for a plain cardboard box of mystery cables, you do want a label — that's when you keep the explicit type.
The key point: the type still exists and is permanent. var never makes a variable "any type". It simply spares you from repeating a type the compiler can already see.
1️⃣ How var Infers a Type
When you write var x = expression;, the compiler looks at the type of expression and gives x exactly that type — permanently. var name = "Ada"; is compiled identically to String name = "Ada";. There is no runtime difference at all.
import java.util.ArrayList;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
// 'var' infers the type from the right-hand side — no repetition.
var greeting = "Hello, var!"; // inferred as String
var count = 42; // inferred as int
var price = 19.99; // inferred as double
var ready = true; // inferred as boolean
System.out.println(greeting);
System.out.println("count is an int? " + (count == 42));
System.out.println("price + tax = " + (price + 1.50));
System.out.println("ready = " + ready);
// The inferred type is fixed — you cannot reassign a String to count.
count = count + 8;
System.out.println("count after += 8: " + count);
// 'var' shines with long generic types:
var scores = new ArrayList<Integer>(); // ArrayList<Integer>
scores.add(90);
scores.add(85);
System.out.println("scores = " + scores);
var ages = new HashMap<String, Integer>();
ages.put("Ada", 36);
System.out.println("ages = " + ages);
}
}2️⃣ var in for Loops
Loops are where var earns its keep. In a classic loop, for (var i = 0; ...) infers int. In an enhanced loop, for (var item : list) infers the collection's element type. Iterating a map's entrySet() normally means typing Map.Entry<String, Integer> — with var it collapses to three letters.
import java.util.List;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
var nums = List.of(10, 20, 30, 40);
// 'var' in an enhanced for loop — n is inferred as Integer.
int sum = 0;
for (var n : nums) {
sum += n;
}
System.out.println("sum = " + sum);
// 'var' in a classic for loop — i is inferred as int.
var line = new StringBuilder();
for (var i = 0; i < nums.size(); i++) {
line.append(nums.get(i));
if (i < nums.size() - 1) line.append(", ");
}
System.out.println("line = " + line);
// Iterating a Map's entries: the long type becomes just 'var'.
// (TreeMap keeps keys sorted, so the output order is stable.)
var capitals = new TreeMap<String, String>();
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");
for (var entry : capitals.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}3️⃣ Readability & the Diamond Trap
Use var when the right-hand side already states the type — var list = new ArrayList<String>(); is clean. Avoid it when the type is hidden behind a method call — var x = service.process(); forces the reader to go hunting. A good explicit type is documentation; don't throw it away.
There is also a subtle trap with the diamond operator <>. Because var infers from the right-hand side, var list = new ArrayList<>(); infers ArrayList<Object> — almost never what you want. Put the type argument on the right.
var bad = new ArrayList<>(); // ArrayList<Object> — trap!
var good = new ArrayList<String>(); // ArrayList<String> — correctimport java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// GOOD use of var: the type is obvious from the right-hand side.
var customers = new ArrayList<String>(); // clearly ArrayList<String>
customers.add("Ada");
customers.add("Linus");
System.out.println("count = " + customers.size());
// var + the diamond operator <>: var needs the type on the RIGHT,
// so prefer the explicit type argument over a bare diamond.
var pairs = new ArrayList<Map.Entry<String, Integer>>();
pairs.add(Map.entry("Ada", 1));
System.out.println("first = " + pairs.get(0));
// QUESTIONABLE use: the type is NOT obvious from a method call.
var result = compute(); // is this an int? a String? a List?
System.out.println("result = " + result);
// Explicit type documents intent when the RHS is unclear:
List<Integer> evens = compute();
System.out.println("evens = " + evens);
}
static List<Integer> compute() {
var out = new ArrayList<Integer>();
for (var i = 0; i <= 8; i += 2) out.add(i);
return out;
}
}4️⃣ Where var Is NOT Allowed
var is strictly a local variable feature. Each line below is a compile error:
class C {
var field = 1; // ❌ fields can't use var
void m(var p) { } // ❌ method parameters can't use var
var m2() { return 1; } // ❌ return types can't use var
void n() {
var x; // ❌ no initializer — nothing to infer
var y = null; // ❌ null has no type to infer
}
}🧠 Quick Recall
Predict the result before opening each answer.
var x = 3 + 4.0;Answer: double. Mixing an int with a double promotes the result to double, so x is 7.0, a double.
var msg = "hi";
msg = 42;Answer: No. msg was inferred as String and that's permanent, so assigning the int 42 is error: incompatible types.
var items = new ArrayList<>();
items.add("apple");Answer: It compiles, but items is ArrayList<Object> because the bare diamond infers Object. You lose type safety on the elements. Fix it: new ArrayList<String>().
🎯 Your Turn — Sum word lengths with var
Use var for both the accumulator and the loop variable to total every word's length.
import java.util.List;
public class Main {
public static void main(String[] args) {
var words = List.of("sky", "is", "blue", "today");
// YOUR TURN: using 'var' for the accumulator AND the loop variable,
// add up the length of every word.
// Expected output:
// Total letters: 14
// var total = ...
// for (var w : words) { ... }
// System.out.println("Total letters: " + total);
}
}🧩 Mini-Challenge — Find the hottest reading
Track a running maximum across a list of temperatures using var throughout.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// MINI-CHALLENGE: find the hottest reading.
// Use 'var' for the list, the running max, and the loop variable.
//
// Expected output:
// Hottest: 30.0
var temps = new ArrayList<Double>();
temps.add(20.0); temps.add(25.5); temps.add(18.0); temps.add(30.0);
// var max = temps.get(0);
// for (var t : temps) { ... }
// System.out.println("Hottest: " + max);
}
}Common Errors
- ❌ Using var on a field or parameter. error: 'var' is not allowed here. Fix: spell out the type — var is locals only.
- ❌ var x; with no initializer. error: cannot infer type for local variable x. Fix: give it a value to infer from.
- ❌ var y = null; null has no concrete type. Fix: use an explicit type, e.g. String y = null;.
- ❌ var list = new ArrayList<>(); infers ArrayList<Object>. Fix: put the type on the right: new ArrayList<String>().
- ❌ Expecting var to be dynamic. It is not — the type is fixed at compile time. Fix: treat it exactly like the explicit type it stands for.
- ❌ Hiding an opaque method's type behind var. Hurts readers. Fix: write the explicit type when the right-hand side doesn't reveal it.
Pro Tips
- 💡 Let the right-hand side carry the type. The best var lines are constructor calls and factory methods where the type is right there.
- 💡 Name variables well. With the type gone, a clear name like customerCount does more of the documenting.
- 💡 var works with lambda parameters since Java 11 — useful only when you need an annotation on the parameter: (@NonNull var x) -> ....
- 💡 Hover in your IDE to see the inferred type instantly — a quick way to confirm var guessed what you expected.
📋 Quick Reference
| Concept | Syntax | Note |
|---|---|---|
| String local | var s = "hi"; | Inferred String |
| int local | var n = 42; | Inferred int, fixed |
| double local | var d = 1.5; | Decimal literal = double |
| Generic list | var l = new ArrayList<String>(); | Type on the RIGHT |
| Classic loop | for (var i = 0; ...) | i is int |
| Enhanced loop | for (var x : list) | x = element type |
| Map entry loop | for (var e : map.entrySet()) | Avoids long Entry type |
| Field | var f = 1; // in class body | ❌ Not allowed |
| Parameter / return | void m(var p) | ❌ Not allowed |
| No initializer | var x; | ❌ Compile error |
| null initializer | var x = null; | ❌ No type to infer |
| Bare diamond | var l = new ArrayList<>(); | ⚠️ Infers Object |
📚 Resources / Keep Learning
- 📘 Oracle: Local Variable Type Inference — the official language guide.
- 📗 dev.java: Using the var Keyword — a guided tutorial with style guidance.
- 📙 JEP 286: Local-Variable Type Inference — the original proposal and rationale.
🎉 Lesson Complete!
You can now use var to drop redundant local-variable types, you know it stays statically typed, and you know exactly where it's forbidden (fields, parameters, returns, and uninitialized or null declarations). You also know to keep the type argument on the right of a generic.
Practice quiz
What does var do in Java?
- Declares a dynamically typed variable
- Creates a global variable
- Infers the variable's static type from its initializer
- Makes a variable mutable
Answer: Infers the variable's static type from its initializer. var keeps Java statically typed — the compiler infers a single fixed type from the right-hand side.
In which Java version was local-variable type inference (var) introduced?
- Java 10
- Java 8
- Java 11
- Java 17
Answer: Java 10. var arrived in Java 10 (JEP 286). Java 11 later allowed it in lambda parameters.
Where is var NOT allowed?
- A local variable inside a method
- A for-loop index
- An enhanced for loop
- A class field (instance variable)
Answer: A class field (instance variable). var works only for local variables, indexes, and loop variables — never for fields, method parameters, or return types.
What happens with: var x;
- x is null
- It fails to compile
- It infers Object
- x is 0
Answer: It fails to compile. var needs an initializer to infer from. var x; has nothing to infer, so it is a compile error.
After var n = 42; can you later write n = "hi";?
- No, n is fixed as int
- Yes, var is dynamic
- Only with a cast
- Only inside a loop
Answer: No, n is fixed as int. var infers int once; the type is permanent, so assigning a String is a compile error.
What type is inferred by var price = 19.99;?
- float
- BigDecimal
- double
- String
Answer: double. A floating-point literal like 19.99 is a double, so price is inferred as double.
Is var a reserved keyword in Java?
- Yes, you can never use it as an identifier
- No, it is a reserved type name — you can still name a method var
- It is deprecated
- Only in modules
Answer: No, it is a reserved type name — you can still name a method var. var is a 'reserved type name', not a full keyword, so older code that used var as a variable or method name still compiles.
Why prefer the explicit type argument with var, e.g. var list = new ArrayList<String>();?
- var requires it for performance
- ArrayList cannot use var
- It is required syntax
- The diamond <> would make the element type Object
Answer: The diamond <> would make the element type Object. var takes its type from the right-hand side, so a bare diamond new ArrayList<>() would infer ArrayList<Object>. Put the type argument on the right.
Which is the BEST use of var for readability?
- var x = compute();
- var map = new HashMap<String, List<Order>>();
- var y = 1;
- var z = service.fetch();
Answer: var map = new HashMap<String, List<Order>>();. var removes the most noise when the right-hand side already states a long, obvious type like HashMap<String, List<Order>>.
Can var be used in an enhanced for loop, like for (var item : items)?
- No
- Only for arrays
- Yes, item is inferred from the collection's element type
- Only for List
Answer: Yes, item is inferred from the collection's element type. var works in both classic and enhanced for loops; the loop variable's type is inferred from the iterable.
Continue this course
- Previous: Final Project Ideas
- Next: Text Blocks — Multi-line string literals with """ — clean JSON, SQL and HTML without escaping
- Quick reference: Java cheat sheet
Frequently asked questions
Does var make Java dynamically typed like Python or JavaScript?
No. Java stays fully statically typed. var only asks the compiler to infer the one correct type from the initializer on the right-hand side, and that type is then fixed forever. var count = 42; makes count an int — you can never assign a String to it. The bytecode is identical to writing int count = 42;. It is pure compile-time convenience, not runtime flexibility.
Where can I use var, and where is it forbidden?
var is only for local variables that have an initializer: ordinary locals, the index of a for loop, the variable of an enhanced for loop, and (since Java 11) lambda parameters. It is NOT allowed for fields (instance or static), method parameters, method return types, or a declaration with no initializer such as var x;. It also can't be used when the right-hand side is null or a lambda/method reference whose type can't be inferred on its own.
When was var added, and is it a keyword?
Local-variable type inference shipped in Java 10 (JEP 286), and Java 11 extended it to lambda parameters. var is a 'reserved type name', not a full keyword — which means old code that happened to use var as a variable or method name still compiles. You cannot, however, name a class var.
Is var bad for readability?
It depends. var is great when the type is obvious from the right-hand side — var users = new ArrayList<User>(); reads cleanly and removes duplication. It hurts readability when the right-hand side is an opaque method call like var x = service.process(); because a reader can no longer see the type at a glance. The rule of thumb: use var when it removes redundant noise, and prefer an explicit type when it documents something the reader couldn't otherwise see.
Why should I write the type argument on the right with var?
Because var infers entirely from the right-hand side. If you write var list = new ArrayList<>(); the bare diamond <> has nothing to infer from and defaults to ArrayList<Object>, which is almost never what you want. Put the real type argument on the right: var list = new ArrayList<String>();. With an explicit declaration you can rely on the diamond, but with var the right side must carry the type.