Local Variable Type Inference

The var keyword lets the compiler figure out a local variable's type from its initializer — it is not dynamic typing, just less typing.

Since Java 10, you can declare a local variable with var instead of writing out its type explicitly. The compiler infers the exact static type from the initializer expression at compile time; the variable remains just as strongly and statically typed as if you had written the type by hand. var is purely a source-code convenience — it changes nothing about how the program runs.

What Is Local Variable Type Inference

var tells the compiler: look at the right-hand side of this declaration and figure out the type yourself. The inferred type is fixed at compile time and baked into the bytecode exactly as if you had spelled it out — there is no runtime type-checking overhead and no dynamic typing involved.

Each var declaration compiles to exactly the same bytecode as the explicit-type version below it.

var is a reserved type name, not a keyword — you can still name a variable, method, or package var, but you cannot use it as a class or interface name.

var name = "Alice";        // inferred as String
var count = 10;             // inferred as int
var prices = new ArrayList<Double>(); // inferred as ArrayList<Double>

// Equivalent to:
String name2 = "Alice";
int count2 = 10;
ArrayList<Double> prices2 = new ArrayList<Double>();

Where var Is Allowed

var is restricted to local variables with an initializer: local variables in methods, constructors, and initializer blocks, the index/element variable of a traditional or enhanced for loop, and resources in a try-with-resources statement. It is NOT allowed for fields, method parameters, method return types, or catch-block exception variables.

for (var i = 0; i < 5; i++) { }              // OK - int
for (var item : List.of("a", "b")) { }        // OK - String
try (var in = new FileInputStream("f.txt")) { } // OK - FileInputStream

// class Bad { private var x = 1; }   // COMPILE ERROR - fields can't use var
// void m(var x) { }                  // COMPILE ERROR - parameters can't use var
// var m() { return 1; }              // COMPILE ERROR - return types can't use var

Initialization Constraints

Because the compiler infers the type from the initializer, a var declaration MUST have an initializer on the same statement, and that initializer cannot be null alone (there is nothing to infer a type from) or a bare array initializer like {1, 2, 3} (its type depends on context the compiler doesn't have here).

// var x;                    // COMPILE ERROR - no initializer
// var y = null;              // COMPILE ERROR - cannot infer from null
var z = (String) null;        // OK - cast supplies the type, String
// var arr = {1, 2, 3};       // COMPILE ERROR - array initializer needs a target type
var arr2 = new int[]{1, 2, 3}; // OK - explicit array creation expression

Apparent Type vs Runtime Behavior

The type inferred for var is called its apparent type, and it behaves identically to writing that type explicitly for every purpose: method resolution, overload selection, assignability, and generic type checking all use the inferred type. var never becomes Object or some dynamically-checked wildcard — it is resolved once, at compile time.

When the initializer's type is an anonymous class expression, var captures that anonymous type plus any members it adds beyond its supertype — a case explicit typing cannot even express, since you can't name an anonymous class.

var list = new ArrayList<String>();
list.add("x");
// list.add(42);   // COMPILE ERROR - list is ArrayList<String>, not raw or Object

var n = 5;
// n = "text";     // COMPILE ERROR - n is int, cannot reassign to a String

Interaction with Diamond and Generics

var combined with the diamond operator <> on the right side can be surprising: without an explicit target type to guide inference, new ArrayList<>() alone infers ArrayList<Object>, not some open generic type. Always give the diamond a concrete type argument (or use a typed factory) when pairing it with var.

var list1 = new ArrayList<>();        // ArrayList<Object> - probably not intended
var list2 = new ArrayList<String>();  // ArrayList<String> - explicit and clear
var list3 = List.of("a", "b");        // List<String> - inferred from List.of's type

Interaction with Anonymous Classes

When var infers the type of an anonymous class instance, the variable's static type includes any extra public members the anonymous class declares beyond its supertype — something you could never write as an explicit declared type, because anonymous classes have no name you can refer to.

If you had written Object counter = new Object() {...} instead, counter.count and counter.increment() would not compile - the static type would just be Object.

var counter = new Object() {
    int count = 0;
    void increment() { count++; }
};
counter.increment();
counter.increment();
System.out.println(counter.count); // 2 - accessible only because var captured the anonymous type

Effect on Numeric Literal Types

var infers the literal's own type, not some "widest reasonable" type. This matters for numeric literals in particular — a plain integer literal infers as int, not long, even when the value is assigned to something conceptually large; you must still use suffixes like L or .0 / f where you want a different primitive type.

var a = 100;      // int
var b = 100L;     // long
var c = 3.14;     // double
var d = 3.14f;    // float
var e = 'x';      // char

Style Guidance: When to Use var

var reduces boilerplate and can improve readability when the type is already obvious from the right-hand side (var users = new ArrayList<User>();) or when the explicit type is long and adds no information (var matcher = pattern.matcher(input);). It hurts readability when the initializer's type is not visually obvious — e.g. it comes from a method call whose return type isn't clear from its name (var result = process(data);).

Common team guidance: prefer var when the type is redundant with the right-hand side or is a long generic type; keep the explicit type when it adds real information a reader would otherwise have to hunt for.

// Good use - type is obvious and verbose to spell out
var orders = new HashMap<String, List<Order>>();

// Questionable use - type is not obvious from the call
var result = compute(); // what type is 'result'? Have to look up compute()'s signature

Practical Usage

var shows up most naturally in loops over generic collections, try-with-resources declarations, and chains of stream/builder calls where the intermediate type is long and self-evident from context.

Map<String, List<Integer>> data = new HashMap<>();
for (var entry : data.entrySet()) {
    var key = entry.getKey();
    var values = entry.getValue();
    System.out.println(key + " -> " + values);
}

Edge Cases

var cannot be used with multiple variable declarations in a single statement (var a = 1, b = 2; is a compile error, even though int a = 1, b = 2; is legal), and it cannot infer a lambda expression's or method reference's type on its own, because those need a functional-interface target type the compiler can't derive purely from the expression.

// var greet = () -> System.out.println("hi"); // COMPILE ERROR - no target type
Runnable greet = () -> System.out.println("hi"); // OK - Runnable supplies the target

Mental Model

Think of var as a compiler macro that expands to the initializer's exact static type before anything else happens — a purely textual/source-level convenience, resolved once at compile time. After that resolution, the variable is indistinguishable from one declared with an explicit type; nothing about type checking, overload resolution, or runtime behavior changes.

Quick Reference

var x = expr;              // local variable, initializer required
for (var i = 0; ...)       // OK in for-loop
for (var e : collection)   // OK in enhanced for
try (var r = open())       // OK in try-with-resources
var f;                     // ERROR - no initializer
var f = null;               // ERROR - cannot infer from null
Field/param/return: var not allowed

Key Takeaways

var infers a local variable's exact static type from its initializer at compile time; it is not dynamic typing.

It requires an initializer on the same statement and cannot infer from bare null or a bare array initializer.

It works only for local variables, for-loop variables, and try-with-resources resources — never fields, parameters, return types, or catch variables.

Pair var with an explicit generic type argument (avoid new ArrayList<>() alone) to avoid accidentally inferring Object.

Use var when the type is obvious or redundant; avoid it when it would hide meaningful information from a reader.

var list = new ArrayList<>(); list.add("hello"); list.add(42); // compiles! list is ArrayList<Object> var list = new ArrayList<String>(); list.add("hello"); // list.add(42); // now correctly a compile error

Without an explicit type argument on the diamond, var infers ArrayList<Object>, silently losing type safety.

var result = fetchData(); // reader has no idea what type this is

UserResponse result = fetchData(); // explicit type documents intent

Use var only where the initializer already makes the type obvious; otherwise it hides information a reader needs.

// var x; x = 5; // COMPILE ERROR var requires the initializer on the same statement - it cannot be declared now and assigned later.

Why is var not considered dynamic typing?

The compiler infers and fixes the exact static type at compile time from the initializer; the variable is exactly as statically typed afterward as if you had written the type explicitly. Nothing about type checking happens at runtime.

Why does var list = new ArrayList<>(); infer ArrayList<Object> instead of some open/generic type?

With no explicit type argument and no other context to constrain it, the diamond operator's inference falls back to Object, since var itself supplies no target type to guide it.

Can you use var for a method parameter? Why or why not?

No. var requires an initializer expression to infer from, and method parameters don't have one at the declaration site - their value comes from the caller at invocation time.

What type does var x = 100L; infer, and how does that differ from var x = 100;?

100L infers long; 100 infers int. var infers the literal's actual type, so numeric suffixes still matter.

Why can't var greet = () -> System.out.println("hi"); compile?

A lambda expression has no type of its own - it needs a target functional interface type (like Runnable) to be assigned to. var can't supply that target type, so there's nothing concrete to infer.

When does var capture something an explicit type declaration cannot express?

When the initializer is an anonymous class instance - var's inferred type includes the anonymous class's own extra members, which have no nameable explicit type.

Is var a = 1, b = 2; legal Java?

No. Unlike explicit-type multi-variable declarations (int a = 1, b = 2;), var does not support declaring multiple variables in a single statement.

Write a loop using var that iterates over a Map<String, Integer> and prints each key-value pair.

Map<String, Integer> scores = Map.of("Ann", 90, "Ben", 85); for (var entry: scores.entrySet()) { var name = entry.getKey(); var score = entry.getValue(); System.out.println(name + ": " + score); } entry is inferred as Map.Entry<String, Integer>, name as String, and score as Integer - all resolved at compile time from Map.of's return type.

What is printed, and what is the inferred type of z?

13.5 is printed. z is inferred as double, because int + double promotes to double under Java's usual binary numeric promotion rules - var just captures whatever type the expression x + y already has.

Does this compile? Why or why not?

No, it does not compile. var infers s as String from its initializer, so s remains statically typed as String forever after - assigning an int to it is a type mismatch, exactly as if you had written String s = "hello";.

Write a try-with-resources statement using var to open a java.io.BufferedReader over a FileReader.

try (var reader = new BufferedReader(new FileReader("data.txt"))) { String line = reader.readLine(); System.out.println(line); } catch (IOException e) { e.printStackTrace(); } reader is inferred as BufferedReader; var is explicitly permitted for try-with-resources resource variables, and the resource is still closed automatically at the end of the try block.

var x = 10;
var y = 3.5;
var z = x + y;
System.out.println(z);
var s = "hello";
s = 42;