Command Palette

Search for a command to run...

PHASE 1Beginner Java 10+ ~30 min· topic 11 of 14

Topic 1.11

Local Variable Type Inference with var

In one line

Since Java 10 you can write var instead of a local variable's type when the initializer already makes the type obvious. The compiler works out the exact type once, at compile time, and the variable keeps that type forever: Java stays statically typed.

Think of it like this

A parcel with a clear window. Normally you write CONTENTS: SHOES on the label. If anyone can already see shoes through the window, writing the label again adds nothing. var lets you skip the label when the contents (the value on the right) make it obvious. The parcel still holds shoes and only shoes; you just didn't write the word.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Type inference
The compiler working out a type for you from the surrounding code, instead of you writing it.
var
A word you write in place of a local variable's type, asking the compiler to infer it from the initializer.
Initializer
The value after = in a declaration, such as 10 in var count = 10;.
Local variable
A variable declared inside a method, constructor or block. Only these can use var.
Static typing
Every variable's type is fixed when the code is compiled. var keeps this: the type is inferred once, then fixed.
Diamond
The empty <> after a generic class name, as in new ArrayList<>(), asking the compiler to infer the type arguments.
Reserved type name
A word that can't be used as a class name but can still be used for variables and methods. var is one.
Target type
The type the surrounding code expects for an expression. Lambdas need one, and var can't supply it.

Step by step

01Same variable, shorter line

These two lines produce exactly the same variable and exactly the same bytecode. With var, the compiler reads the right-hand side, sees an ArrayList<String>, and gives names that type.

The win grows with long generic types: Map<String, List<Integer>> scores = new HashMap<String, List<Integer>>(); repeats itself, and var scores = new HashMap<String, List<Integer>>(); says it once.

Main.javawhole filejava
ArrayList<String> names = new ArrayList<String>();   // type written twice
var names2 = new ArrayList<String>();                // type written once, same result

02What the compiler does

During compilation, javac type-checks the initializer first, then copies its type to the variable. From that moment the variable behaves exactly as if you had written the type: every later assignment and method call is checked against it.

Nothing happens at run time. The JVM never sees var; it sees an ordinary local slot of type int, String or whatever was inferred. That is why var costs nothing in performance.

What the compiler doesdiagram
Rendering diagram…

03The literal decides the exact type

The inferred type is the type of the expression, nothing wider or narrower. var b = 5; gives an int, even if you meant a byte. var total = 0; followed by adding long values fails, because total is an int.

When the precise type matters, either use a suffix (0L, 0.0, 0.0f) or write the type. long total = 0; is clearer than var total = 0L; for many readers.

Main.javawhole filejava
var a = 5;       // int
var b = 5L;      // long
var c = 5.0;     // double
var d = 5.0f;    // float
var e = 'x';     // char
var f = "x";     // String
var g = 5 / 2;   // int, value 2

04Why var needs an initializer

var total; has nothing to infer from, so it's rejected. var name = null; is rejected too: null fits every reference type, so there's no single answer.

An array initializer {1, 2, 3} is not an expression with a type of its own; it only means something when the declared type says 'int array'. Write var nums = new int[] {1, 2, 3}; instead. A lambda such as () -> "hi" is in the same position: it needs a target type to know which interface it implements (Phase 10).

05The diamond trap

The diamond <> infers type arguments from the left-hand side. With var there is no left-hand type, so new ArrayList<>() falls back to ArrayList<Object>. The code compiles, and then happily accepts a String and an Integer in the same list, which is usually a bug waiting to happen.

Rule: with var, put the element type on the right: var names = new ArrayList<String>();. Generics are covered in Phase 8.

06var in loops

var shines in loops over collections, where the element type can be long. for (var entry : scores.entrySet()) replaces for (Map.Entry<String, Integer> entry : scores.entrySet()).

In a counting loop, for (var i = 0; i < n; i++) makes i an int, which is what you'd write anyway.

07When not to use var

var hides the type from the reader. That's fine when the right side shows it, and harmful when it doesn't: var result = process(data); forces the reader to look up process to know what result is.

Good: var reader = new BufferedReader(...), var count = 0, var entry : map.entrySet(). Better with a type: results of methods whose name doesn't reveal the type, numbers where int vs long matters, and anywhere you want to program to an interface (List<String> names = new ArrayList<>(); deliberately declares the variable as a List, while var would make it an ArrayList).

Try it yourself

  1. 1

    Prove the type with behaviour

    In the first example, add var half = count / 3; and print it. Predict first: count is 15 at that point and both sides are int, so the answer is 5. Then change it to var half = count / 3.0; and predict again (5.0).

  2. 2

    Make var refuse

    Add var nothing; on a line of its own and compile. Then try var empty = null;. Read both error messages: each one says why there was nothing to infer from.

    terminal
    $ javac Main.java
    ── expected output ──
    Main.java:3: error: cannot infer type for local variable nothing
    var nothing;
    ^
    (cannot use 'var' on variable without initializer)
    1 error
  3. 3

    See that the bytecode is the same

    Write a class with int a = 1; and compile it, then change it to var a = 1; and compile again. Run javap -c Main both times and compare: the instructions are identical (iconst_1, istore_1).

Code & diagrams

The initializer decides the type New tab
Sign in to run this example in your browser.

Expected output

count / 4    = 2
price / 4    = 2.5
big + 1      = 3000000001
letter + 1   = 66
name.length  = 4
count now    = 15
var in loops over collections New tab
Sign in to run this example in your browser.

Expected output

hello Asha
hello Ravi
Asha -> 91
Ravi -> 72
1+2+3+4 = 10
The diamond trap New tab
Sign in to run this example in your browser.

Expected output

anything: [text, 42]
words:    [text]
Two curiosities: a variable named var, and an anonymous type New tab

With `Object point = new Object() { ... };` the line `point.x` would not compile, because `Object` has no field `x`. This trick is legal but rarely a good idea in real code.

Sign in to run this example in your browser.

Expected output

still legal
x + y = 7
var on lambda parameters Java 11+ New tab

Writing `(a, b) -> a + b` does the same. The `var` form exists so you can annotate parameters, as in `(@Nonnull var a, @Nonnull var b) -> a + b`. Either all parameters use `var` or none do.

Sign in to run this example in your browser.

Expected output

2 + 3 = 5

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Change the type of a var variable

Write var price = 10; and later price = 12.5;.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        var price = 10;
        price = 12.5;
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: incompatible types: possible lossy conversion from double to int
price = 12.5;
^
1 error

Break #2

Use var for a field

Declare var count = 0; directly inside the class, outside any method.

Main.javawhole filejava
public class Main {
    var count = 0;
    public static void main(String[] args) {
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:2: error: 'var' is not allowed here
var count = 0;
^
1 error

Break #3

Declare two vars in one statement

Write var a = 1, b = 2;.

terminal
$ javac Main.java
── what you'll see ──
Main.java:3: error: 'var' is not allowed in a compound declaration
var a = 1, b = 2;
^
1 error

Myth vs fact

Myth

var makes Java dynamically typed, like JavaScript's var.

Fact

The type is inferred once at compile time and fixed forever. Assigning a value of another type is a compile error.

Myth

var is slower because the type is found at run time.

Fact

Inference happens in javac. The bytecode is identical to writing the type yourself, so there is zero run-time cost.

Myth

var is a new keyword, so old code with a variable named var breaks.

Fact

It is a reserved type name. Variables and methods named var still compile; only types named var are forbidden.

Myth

var list = new ArrayList<>(); gives a list of whatever I add later.

Fact

It gives ArrayList<Object>, decided on that line. Put the element type on the right: new ArrayList<String>().

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    var was added by JEP 286 in Java 10; JEP 323 in Java 11 extended it to lambda parameters. JLS 14.4.1 defines the inferred type as the upward projection of the initializer's type, which removes capture variables (for example from List<?>).

  • ▸

    var can hold non-denotable types, types you can't write in source: an anonymous class type (its extra members stay accessible), or an intersection type, as in var x = flag ? 1 : "s";, whose type combines Serializable, Comparable and other interfaces common to Integer and String.

  • ▸

    Because the variable takes the concrete type, var list = new ArrayList<String>(); exposes ArrayList-only methods such as ensureCapacity and trimToSize. Code that should depend only on an interface should declare List<String> explicitly.

  • ▸

    IDE tooling and javap -l (with javac -g) show the inferred types in the LocalVariableTable; there is no var anywhere in the class file. OpenJDK's 'Local Variable Type Inference: Style Guidelines' (Stuart Marks) is the de-facto reference for when to use it.

Remember this

  1. 1

    var works only for local variables that are initialized on the same line: var count = 10;. The compiler looks at the type of the initializer (10 is an int) and uses that exact type as if you had typed int count = 10;. This is called type inference.

  2. 2

    var does not make Java dynamic. The type is chosen once, at compile time, and never changes. After var count = 10;, the line count = 12.5; is still an error, because count is an int. The class file is identical to the one you'd get by writing the type yourself: var leaves no trace in the bytecode.

  3. 3

    The inferred type is exactly the initializer's type, which matters for literals: var n = 5; is int (not byte or long), var d = 5.0; is double, var f = 5.0f; is float, var big = 5L; is long, var c = 'A'; is char. With generics, var list = new ArrayList<>(); infers ArrayList<Object>, because the diamond has nothing to go on; write new ArrayList<String>() instead.

  4. 4

    Where var is not allowed: fields, method parameters, method return types, a declaration with no initializer (var x;), an initializer of null, an array initializer (var a = {1, 2};), a lambda or method reference with no target type, and several variables in one statement (var a = 1, b = 2;). Each of these gives the compiler nothing exact to infer from, or var was deliberately kept local.

  5. 5

    Where var is allowed and useful: ordinary locals, the index of a classic for loop, the variable of an enhanced for loop (for (var entry : map.entrySet())), resources in try-with-resources (Topic 7.6), and, since Java 11, lambda parameters ((var a, var b) -> a + b), which mainly exists so you can put annotations on them.

  6. 6

    var is a reserved type name, not a keyword. Old code that used var as a variable or method name still compiles (var var = "ok"; is legal), but no class or interface may be named var any more. The style rule most teams follow, from OpenJDK's own guidelines: use var when the right-hand side shows the type (new, a factory method with a clear name, a literal), and spell the type out when it would make the reader guess.

Explain it without notes

01

What does var do, and why is Java still statically typed when you use it?

02

List the places where var can't be used and give the reason for two of them.

03

What type does var list = new ArrayList<>(); have, and why is that a problem?

04

When would you choose to write the type instead of var, even though var would compile?

Practice

01

Rewrite this with var wherever the type stays obvious: String city = "Delhi"; int pop = 32; double area = 1484.0; and print pop / area per square km (just the raw division).

02

Use var to build a TreeMap<String, Integer> of three fruits and their prices (apple 120, banana 40, cherry 300), then loop with var over the entries and print each, followed by the total.

03

Show that var keeps the literal's exact type: declare var small = 7; and var large = 7L;, and print small * 1_000_000_000 and large * 1_000_000_000.

Trade-offs

  • ↔

    var removes noise from long generic declarations and keeps the variable name at a fixed column, but it hides the type; that is a gain when the right side shows the type and a loss when it doesn't.

  • ↔

    var gives the concrete type (ArrayList), while an explicit declaration can choose an interface (List); the interface keeps later code from depending on implementation details.

  • ↔

    With literals, var silently picks int or double; writing the type makes the numeric range visible, which matters for sums and money.

Done when you can

  • I can say exactly where var is allowed and where it isn't.

  • I can predict the inferred type of literals and new expressions.

  • I know var is resolved at compile time and leaves no trace in bytecode.

  • I avoid the new ArrayList<>() trap with var.

  • I can decide when var helps readability and when an explicit type is better.