var

var (Java 10+) lets the compiler infer a local variable's type from its initializer, cutting boilerplate without turning Java into a dynamically typed language.

var (introduced in Java 10) enables local variable type inference: instead of writing out a variable's type explicitly, you write var, and the compiler determines the concrete static type from the initializer expression at compile time. The variable remains as strongly and statically typed as if you'd written the type yourself.

What var Is: Compile-Time Type Inference

var is purely a source-code convenience. The compiler looks at the initializer expression, determines its type, and substitutes that exact type into the compiled bytecode — there is no var type at runtime, and no performance difference between var x = 5; and int x = 5;. The .class file is identical either way.

var count = 10;              // inferred as int
var name = "Claude";         // inferred as String
var list = new ArrayList<String>(); // inferred as ArrayList<String>
var map = new HashMap<String, List<Integer>>(); // inferred as HashMap<String, List<Integer>>

var Is Not Dynamic Typing

This is the single most important thing to understand about var: it does not make Java dynamically typed. Once the compiler infers the type at the declaration, that variable is fixed to that type for its entire scope — you cannot later assign it a value of an incompatible type, exactly as with an explicitly typed variable.

Contrast with genuinely dynamically-typed languages (e.g. Python, JavaScript) where a variable's type can change at runtime with each assignment. Java's var only affects what you type in the source; the compiler-checked, fixed type is still there.

var message = "hello"; // inferred as String
message = 42;           // compile-time error: incompatible types: int cannot be converted to String

Initialization Requirement

Because the compiler needs an expression to infer the type from, var requires an initializer at the point of declaration — you cannot declare var x; and assign later. Similarly, var cannot be initialized with null alone (there is no type to infer from a bare null), and cannot be used for array-type declarations using the bracket-after-name form.

var x;              // compile-time error: cannot infer type
var y = null;        // compile-time error: cannot infer type
var arr = new int[5]; // OK: inferred as int[]
var[] arr2 = new int[5]; // compile-time error: var cannot be an array element type

Where var Can and Cannot Be Used

var is restricted to specific contexts: local variables with initializers, enhanced for-loop index/element variables, traditional for-loop local index variables, and local variables in try-with-resources. It cannot be used for fields, method parameters, method return types, or catch-block exception variables.

class Demo {
    // var field;        // compile-time error: not allowed for fields

    // void m(var x) {}  // compile-time error: not allowed for parameters

    void run(List<String> items) {
        for (var i = 0; i < items.size(); i++) { }   // OK
        for (var item : items) { }                     // OK
        try (var scanner = new java.util.Scanner(System.in)) { } // OK
    }
}

var With Generics and Diamond Operator

When using var with generics, be explicit about type arguments on the right-hand side (new ArrayList<String>()), not the empty diamond <> — var list = new ArrayList<>(); infers list as ArrayList<Object>, because there's no left-hand-side target type left for the diamond to infer against, which is very likely not what you intended.

var list1 = new ArrayList<String>();  // ArrayList<String> - correct
var list2 = new ArrayList<>();        // ArrayList<Object> - probably a bug!
list2.add("ok");   // compiles (Object accepts anything)
list2.add(42);     // also compiles - no longer type-safe for String usage

Readability: When var Helps and When It Hurts

var improves readability when the type is already obvious from the right-hand side (var list = new ArrayList<Order>();, var user = repository.findById(id); with a well-named variable) — it removes redundant noise. It hurts readability when the initializer's type isn't obvious from context (a method call whose return type you can't guess), or when the variable name is vague, forcing the reader to hover in an IDE or check the method signature to know the type.

// Reads fine - type is obvious
var orders = new ArrayList<Order>();
var name = user.getFirstName();

// Reads poorly - type is not obvious from the call alone
var result = process(data); // what type is result? have to go check process()'s signature

var and Numeric Literals

With numeric literals, var infers the narrowest applicable literal type by Java's normal literal rules — an integer literal infers int, not long or byte, unless you use a suffix. This can be surprising if you expected a wider type.

var a = 10;      // int
var b = 10L;     // long (suffix required)
var c = 10.0;    // double
var d = 10.0f;   // float (suffix required)
var e = (byte) 10; // byte, via explicit cast

var Does Not Affect Immutability

var says nothing about mutability — combine it with final exactly as you would an explicitly typed variable if you want to prevent reassignment: final var count = 0;.

final var name = "Claude"; // cannot be reassigned
// name = "Other"; // compile-time error

Complete Example

import java.util.*;

class VarDemo {
    void run() {
        var names = List.of("Ann", "Bo", "Cy");
        var total = 0;
        for (var name : names) {
            total += name.length();
        }
        var summary = "Total chars: " + total;
        System.out.println(summary); // Total chars: 7
    }
}

Key Takeaways

var is compile-time local variable type inference, not dynamic typing — the inferred type is fixed and checked at compile time.

var requires an initializer (never var x; and never a bare var x = null;).

var is only for local variables, for-loop variables, and try-with-resources — never fields, parameters, or return types.

Be explicit about generic type arguments when using var with new; the empty diamond infers Object.

Use var when the type is obvious from context and the variable is well-named; avoid it when it would obscure the type.

var list = new ArrayList<>(); // inferred as ArrayList<Object> list.add("text"); list.add(42); // compiles, but breaks type safety intent var list = new ArrayList<String>(); // inferred as ArrayList<String> list.add("text"); // list.add(42); // compile-time error, as intended

The diamond operator <> needs a target type to infer against; without an explicit left-hand-side type (since var provides none), it defaults to Object, silently losing type safety.

var x; // compile-time error x = 5;

var x = 5; // type inferred immediately from the initializer var requires the type to be inferable at the declaration itself; there is no way to declare now and initialize later.

var result = compute(); // reader has no idea what type this is without checking compute()'s signature

OrderSummary result = compute(); // or rename: var orderSummary = compute();

var should be used where the type is already clear from the right-hand side or a well-chosen variable name — otherwise it removes useful information from the reader without benefit.

Does using var make Java dynamically typed? Explain.

No. var only tells the compiler to infer the type from the initializer at compile time; once inferred, the variable has a fixed, statically-checked type for its whole scope, exactly like an explicitly typed variable. Only the source-code spelling changes, not Java's type system.

Why does var x; fail to compile?

The compiler needs an initializer expression to determine what type to infer; without one (or with just = null), there is no type information available, so var cannot be used.

Where can var be used, and where is it disallowed?

var can be used for local variables with an initializer, for-loop and enhanced-for-loop variables, and try-with-resources variables. It cannot be used for class/instance fields, method parameters, method return types, or catch-block exception variables.

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

It infers ArrayList<Object>, because the diamond operator needs a target type to infer its generic argument from, and var provides none on the left-hand side. This silently loses the type safety you likely intended, e.g. allowing both add("text") and add(42).

Does var count = 0; allow count to later be assigned a String?

No. count is fixed as int at the point of inference; assigning a String to it afterward is a compile-time type error, just as if you'd declared int count = 0; explicitly.

When is var considered good style, and when should it be avoided?

It's good style when the type is obvious from the initializer or from a well-named variable, removing redundant noise. It should be avoided when the initializer's type isn't obvious (e.g. an ambiguously-named method call) or the variable name is vague, since that combination hides useful information from readers.

How do you make a var-declared variable immutable (non-reassignable)?

Combine var with final, exactly as you would with an explicit type: final var name = "Claude"; prevents reassignment while still inferring the type from the initializer.

What happens when this code compiles?

Compile-time error: incompatible types: String cannot be converted to int. x was inferred as int from its initializer (5), and that type is fixed — assigning a String to it fails just as it would for an explicitly declared int.

Write a for-each loop over a List<Integer> using var for the loop variable, summing the values.

List<Integer> nums = List.of(1, 2, 3, 4); var sum = 0; for (var n : nums) { sum += n; } System.out.println(sum); // 10

10 — var infers int for sum and Integer (auto-unboxed in the += context) for n, and the loop sums all elements.

Does this compile? What type is map inferred as?

It compiles. map is inferred as HashMap<Object, Object> because the diamond <> has no target type to infer generics from, so both put calls (mixed key/value types) are legal — illustrating why explicit generic arguments matter with var.

Write code demonstrating that var with a numeric literal infers the narrowest default literal type (int), requiring a suffix for long.

var a = 100; // int var b = 100L; // long System.out.println(((Object) a).getClass().getSimpleName()); // Integer System.out.println(((Object) b).getClass().getSimpleName()); // Long

Integer, then Long — without a suffix, an integer literal is always int by Java's literal rules; var just reflects that, it doesn't change literal typing rules.

var x = 5;
var y = "hello";
x = y;
var map = new HashMap<>();
map.put("a", 1);
map.put(2, "b");
System.out.println(map);