Skip to content
Part IA Michaelmas Term

Local Variable Type Inference

What var Does

var, introduced in Java 10, performs local variable type inference. The compiler deduces the static type of a local variable from the type of its initialiser. The programmer writes var instead of an explicit type declaration, and the compiler fills in the type at compile time.

var message = "Hello";          // infers String
var count = 42;                 // infers int
var price = 9.99;               // infers double
var names = new ArrayList<String>(); // infers ArrayList<String>

The compiled bytecode is identical to what it would be with explicit types. var is purely a convenience for the programmer — it does not introduce dynamic typing.

This Is Still Static Typing

This is a crucial point for Tripos: var does not make Java dynamically typed. The type is fixed at compile time, exactly as if you had written the type explicitly. You cannot reassign a var variable to a value of an incompatible type:

var x = 42;     // x is int
x = "hello";    // COMPILE ERROR: incompatible types: String cannot be converted to int

This is entirely different from JavaScript’s var or Python’s untyped variables. In those languages, the variable can hold any type at any time. In Java with var, the type is inferred once and enforced thereafter.

Where var Works

var can only be used for local variables with initialisers:

var s = "text";                          // OK — local variable with initialiser
for (var item : collection) { ... }      // OK — enhanced for loop
var result = switch (x) { ... };         // OK — switch expression (Java 14+)

Where var Does NOT Work

var cannot be used for:

  • Fields (instance or static variables): the compiler needs the explicit type in the class declaration
  • Method parameters: the method signature must declare parameter types explicitly
  • Method return types: the return type is part of the method’s contract
  • Uninitialised local variables: var x; is a compile error — there is nothing to infer from
class Example {
    var field = 42;          // COMPILE ERROR: var not allowed for fields

    public var compute() {   // COMPILE ERROR: var not allowed for return type
        return 42;
    }

    public void method(var x) { // COMPILE ERROR: var not allowed for parameters
        var y;                  // COMPILE ERROR: cannot infer type without initialiser
    }
}

When to Use var

Good Uses

var shines when the type is obvious from the right-hand side and writing it out adds no value:

var scanner = new Scanner(System.in);
var map = new HashMap<String, List<Customer>>();
var stream = files.stream().filter(f -> f.isFile());

The new keyword makes the type immediately clear to the reader. Similarly, factory methods with descriptive names (Files.newBufferedReader(...)) are self-documenting.

Bad Uses

var hurts readability when the type is not obvious:

var result = process(data);    // What type is result? Must look up process().
var x = getSomething();        // Completely opaque.

A reader should not need to navigate to the method signature to understand what type a variable holds.

Tripos Relevance

The exam may test your understanding that:

  1. var is compile-time inference, not runtime dynamic typing
  2. The compiler must be able to determine a unique, concrete type from the initialiser
  3. var cannot be used for fields, parameters, or return types
  4. The inferred type is the static type of the initialiser expression, not the runtime type of the object

Consider this case:

Animal pet = new Dog();
var pet2 = new Dog();

pet has static type Animal (the declared type of the variable). pet2 has static type Dog (the inferred type from new Dog()). This can affect which overloaded methods are called and which fields are visible.

Type Inference with Diamond Operator

Be careful with the diamond operator:

var list = new ArrayList<>();      // infers ArrayList<Object>, not ArrayList<String>

Without a type argument, the diamond defaults to Object. If you want a specific generic type with var, provide it:

var list = new ArrayList<String>();  // infers ArrayList<String>