Skip to content
Ameen Abozaid
PublishedBEG-009-L06

Local type inference with var

  • Lesson 6 of 7
  • 12 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

Writing the type on every declaration is sometimes repetitive. In String name = "Ana"; the right side already says "this is a String". Since Java 10 you may let the compiler infer the type of a local variable from the value you give it, by writing var instead of the type:

java
var name = "Ana";

This is a convenience and nothing more. The box is still built for one specific type, and the compiler chose it for you. In box terms: instead of writing "this is a text box" on the label yourself, you hand over the text and the compiler writes the label. The label still can never change.

Why this matters: you will meet var in real codebases, and you must know both what it does and what it does not. It does not make Java dynamic. Used well it removes noise; used badly it hides the types a reader needs. Knowing when to choose which is part of writing code that teammates can read.

This lesson builds on Lessons 1 to 5. It needs the idea that each literal has a type (Lesson 4) and that types never change (Lesson 1). It also touches the conversion rules from Lesson 5 once.

Learning Objectives

By the end of this lesson you will be able to:

  • Write a local variable declaration with var and say which type the compiler infers for a literal.
  • Explain that var does not make a variable dynamic: the inferred type is fixed.
  • State the rules: local variables only, and an initializer on the same line, which cannot be null.
  • Read the compiler messages when var is misused.
  • Decide when var is clear and when an explicit type is better.

Written Explanation

What var does

var asks the compiler to look at the right-hand side, work out its type, and use that type for the variable. The result is exactly the same as if you had written the type yourself.

With varThe inferred typeSame as writing
var count = 10;intint count = 10;
var price = 9.99;doubledouble price = 9.99;
var name = "Ana";StringString name = "Ana";
var big = 3_000_000_000L;longlong big = 3_000_000_000L;
var ratio = 0.5f;floatfloat ratio = 0.5f;
var initial = 'A';charchar initial = 'A';
var done = false;booleanboolean done = false;

Notice that this is why Lesson 4's literal rules matter. var count = 10; gives an int, because 10 is an int literal. var big = 3_000_000_000L; gives a long only because of the L. You cannot get a long or a float through var without writing the suffix.

The type is still fixed

var is not a "any type" box. It is not like variables in languages such as JavaScript. Once the compiler has chosen the type, that type is permanent, and the usual checks apply:

java
var count = 10;
count = "ten";    // error: incompatible types: String cannot be converted to int
var n = 5;
n = 2.5;          // error: possible lossy conversion from double to int

The Java tutorial says it plainly: the compiler infers the type from the initializer at compile time and the variable keeps it. Only how you spell the declaration changed.

The rules

var has firm limits. The compiler enforces them:

  1. Local variables only. It is for variables declared inside a method (and a few related places such as loop variables). It cannot be used for fields of a class, for method parameters, or for return types.
  2. An initializer is required. The compiler needs a value to infer from, so var a; is an error:
text
error: cannot infer type for local variable a
  (cannot use 'var' on variable without initializer)
  1. The initializer cannot be null. null fits every reference type, so there is nothing to infer:
text
error: cannot infer type for local variable b
  (variable initializer is 'null')

When var is a good idea

The official style guidance for var boils down to one test: does it make the code easier to read?

  • Good: the right side shows the type. var name = "Ana"; and var count = 10; are obvious. In var list = new ArrayList<String>(); (a class you will learn later) the type already appears on the right, so repeating it on the left is noise.
  • Good: a clear name does the rest. The guidance asks you to give a var a descriptive name, because the type is no longer written beside it.
  • Better to avoid: the right side hides the type. Consider var result = process(input);. What is result: a number, a text, an object? A reader has to find the method to know. Writing the type makes it instantly clear.
  • Avoid: when you want a type different from the default. var total = 0; is an int. If you need a long or a double, write long total = 0; or 0.0 explicitly.

The decision is about readers, not typing. In this course, use var only when the right-hand side is a literal or a plain constructor, and write the type explicitly otherwise.

One more detail: what type will the expression have?

The compiler uses the normal rules of Lesson 5 to find the type of the right-hand side. If the right side is 1 / 2, both operands are int, so the result is an int, 0. If you wanted 0.5 you need to change the expression, not the var:

java
var half = 1 / 2;      // int, value 0
var half2 = 1 / 2.0;   // double, value 0.5

var never decides to be a double just because you wished it to be.

Examples

Example 1: var with literals (simple)

java
public class VarBasics {
    public static void main(String[] args) {
        var count = 10;
        var price = 9.99;
        var name = "Ana";
        var big = 3_000_000_000L;
        var ratio = 0.5f;
        var initial = 'A';
        var done = false;
        var total = count * price;
        System.out.println(count + " " + price + " " + name + " " + big + " " + ratio
            + " " + initial + " " + done + " " + total);
        System.out.println(((Object) count).getClass().getName());
        System.out.println(((Object) total).getClass().getName());
    }
}

Output (JDK 21):

text
10 9.99 Ana 3000000000 0.5 A false 99.9
java.lang.Integer
java.lang.Double

The last two lines are a trick to ask what type a value has at run time. They print the wrapper names Integer and Double because the cast to Object boxes the primitive; you can read that as "the inferred types are int and double". (Wrappers are a later topic; you can ignore the details.) Notice total: it multiplied an int by a double, so by the widening rule of Lesson 5 the compiler inferred double.

Example 2: the compiler rejects misuse (practical)

java
public class VarErrors {
    public static void main(String[] args) {
        var a;
        var b = null;
        var count = 10;
        count = "ten";
        var n = 5;
        n = 2.5;
    }
}

JDK 21 reports four errors:

text
VarErrors.java:3: error: cannot infer type for local variable a
        var a;
            ^
  (cannot use 'var' on variable without initializer)
VarErrors.java:4: error: cannot infer type for local variable b
        var b = null;
            ^
  (variable initializer is 'null')
VarErrors.java:6: error: incompatible types: String cannot be converted to int
        count = "ten";
                ^
VarErrors.java:8: error: incompatible types: possible lossy conversion from double to int
        n = 2.5;
            ^
4 errors

The first two violate the rules (no initializer, null initializer). The last two show that the inferred type is fixed: count is an int forever, and n cannot take a double.

Example 3: var does not rescue a wrong expression (realistic)

java
public class VarHalf {
    public static void main(String[] args) {
        var half = 1 / 2;
        var half2 = 1 / 2.0;
        System.out.println(half + " " + half2);

        long x = 5;
        var y = x;
        y = 6;
        System.out.println(y);
    }
}

Output:

text
0 0.5
6

half is an int because both operands were; var kept the same truncation bug as an explicit int would. y takes the type of x, long. If we later wrote var n = 5; n = 7L; it would fail with possible lossy conversion from long to int, because n was inferred as an int. The lesson is the same as always: know the type of the expression. var just means you no longer write it.

Backend Insight

You will see var constantly in modern Java backend code, often in lines like var response = client.send(request); or with long generic types where the explicit type would be three times the length of the line. The tool is genuinely useful there.

The rules of good use matter more in backend code than in small exercises:

  • Code review. Reviewers often read code in a web page or a diff without an IDE to show inferred types. If a line only makes sense when you hover over the variable, the reviewer is blind. Prefer explicit types when the right-hand side is a method call whose result type is not obvious from its name.
  • Refactoring risk. If a method's return type changes from int to long, a var variable silently changes type too. That can be wanted or dangerous (it can bring overflow or conversion issues back from Lesson 5). An explicit type makes such a change fail to compile at the right place.

Many teams settle on "use var for obvious constructors and literals; spell out the type when it carries information".

Common Mistakes

Mistake 1: Treating var as "any type"

var x = 10; x = "hello"; fails. var only saves typing; the type is fixed at compile time.

Mistake 2: var without a value

var total; fails: cannot infer type for local variable total. Write the type, or give a value: var total = 0;.

Mistake 3: Initializing with null

var name = null; fails with variable initializer is 'null'. Write String name = null; if "no value yet" is really what you mean.

Mistake 4: Using var where it hides the type

var data = load(); forces the reader to look up load. Write the type, or at least choose a descriptive name such as var customers = loadCustomers();.

Mistake 5: Expecting a different numeric type

var total = 0; is an int, and var rate = 5 / 2; is 2. If you want a long or a double, write the suffix or the type explicitly: var total = 0L;, 5 / 2.0.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. What type does the compiler infer for each: var a = 7;, var b = 7.0;, var c = "7";, var d = '7';, var e = 7L;?

Show solution A1

A1. int, double, String, char, long.

A2. Is this allowed: var x;? Why or why not?

Show solution A2

A2. No. var needs an initializer on the declaration to infer from; the compiler says it cannot infer the type because there is no initializer.

A3. Is var allowed for a method parameter? For a local variable inside main?

Show solution A3

A3. Not allowed for a method parameter. Allowed for a local variable such as inside main.

Level B: Application

B1. Rewrite these with var where the type is obvious from the right side. Say why you would leave any unchanged.

java
int attempts = 3;
String user = "ana";
double rate = compute();
boolean active = true;
Show solution B1

B1. Possible rewrite: var attempts = 3;, var user = "ana";, var active = true;. Leave double rate = compute(); unchanged because the right side is a method call and the reader cannot tell the type from it (and var would hide that it is a double).

B2. What is the type and value of var r = 9 / 4;? Change one thing so that r is 2.25.

Show solution B2

B2. int, value 2, because it is integer division. Change 4 to 4.0: var r = 9 / 4.0; gives the double 2.25.

B3. The code below fails to compile. Explain why and give two different fixes.

java
var level = 1;
level = 1.5;
Show solution B3

B3. level was inferred as int, so assigning the double 1.5 is a lossy conversion. Fix 1: declare it as a decimal from the start, var level = 1.0; (then level = 1.5; works). Fix 2: use an explicit double level = 1; (the 1 is widened automatically), then assign 1.5.

Level C: Challenge

C1. A teammate says var is "dynamic typing in Java". Write three sentences that show why that is wrong, using an example that fails to compile.

Show solution C1

C1. var only lets the compiler fill in the type at compile time; the type is not allowed to change afterwards. For example var n = 5; n = "five"; does not compile, while in a dynamically typed language this would run. The type is known and checked before the program runs, which is static typing.

C2. A method getCount() returns an int. Your code has var count = getCount(); and later does count * 1000000. The method is changed to return a long. Describe what changes in your code without you editing it, and one reason why you might prefer writing int count = getCount(); in a critical calculation.

Show solution C2

C2. count silently becomes a long. Then count * 1000000 is computed as a long and no longer overflows, but anything else that assumed int (for example passing it to something that needs an int) may start failing to compile elsewhere. Writing int count = getCount(); makes the change fail to compile right at that line, which tells you immediately that a type changed, so you can decide on purpose whether a narrowing cast is acceptable.

Mini Challenge

Take the five lines below and decide for each whether var improves it. Rewrite the program using var only where the type is obvious, and add a one-line comment above each unchanged declaration explaining why you left it. Then add one last line that tries to assign a String to a var integer and note, in a comment, the compiler's complaint.

java
int quantity = 12;
double price = 4.5;
String label = "Notebook";
long id = 8_000_000_000L;
double discount = lookupDiscount(label);

Check your work: the four literal lines use var (and id keeps its L); the method-call line stays explicit, with a comment; and your comment on the final line names the type mismatch.

Summary & Next Lesson

Key takeaways

  • var lets the compiler infer a local variable's type from its initializer. The type is still static and fixed.
  • It works only for local variables, and needs an initializer that is not null.
  • Literals decide the type: 10 is int, 10L is long, 1.5 is double, 1.5f is float.
  • Use var when the right side makes the type obvious; use a written type when it would hide it.
  • var cannot fix a wrong expression: 1 / 2 is still 0.

Next lesson

Next is "Demo: unit converter and an integer overflow". You will put the whole module together in a small program: temperatures, distances, money in cents, a planned integer-division mistake, and a deliberate overflow.

Self-check

  1. What is the type of var x = 3.5;?
  2. Why does var a; fail?
  3. Can you write var s = "hi"; s = 5;?
  4. Where is var not allowed: local variables, fields, or both?
  5. Give one case where an explicit type is better than var.
Show answers
  1. double.
  2. There is no initializer, so the compiler cannot infer the type.
  3. No. s is a String for its whole life; 5 is an int, so the assignment is an incompatible types error.
  4. It is allowed on local variables and not allowed on fields (or parameters).
  5. When the right side is a method call whose result type is not obvious, or when you need a type that differs from the literal default, such as long total = 0;.

References

  • Oracle, Java Language Updates, Java SE 21: Local Variable Type Inference. It says var is for local variables (and some related places), requires a non-null initializer, infers a type that stays static.
  • OpenJDK Amber project, Style Guidelines for Local Variable Type Inference. It advises descriptive names, small scopes, using var when the initializer makes the type clear, noting that integer literals infer int, and judging code by readability for later readers, including those who read it without an IDE.
  • The compiler messages and outputs above were produced with JDK 21. The suggestion that a var variable follows a changed return type and that explicit types make that visible is standard reasoning about inference, not a quoted source. The course rule "use var only for literals and plain constructors" is this course's own teaching policy, not an official one.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.