Skip to Content

Java Developers: var Is Your Friend

Clearing up confusion around Local Variable Type Inference

Posted on
Photo by Geoff Greenwood on Unsplash
Photo by Geoff Greenwood on Unsplash

Java 10 has introduced new syntax for local variable type inference that I see people are confused about. In this post, I want to take a moment to try and convince you that things are better than they seem.

What is Type Inference?

Did you know we already infer types in Java? The diamond operator was introduced way back in Java 7 and is a form of type inference. Let’s look at an example:

public void doSomething() {
    final List<String> names = new ArrayList<String>();
                                         //  ^^^^^^------- Redundant
    ...
}

In this case, telling the compiler which type the ArrayList handles isn’t necessary because it is able to infer this from the hints already given. Namely, the left hand side of the assignment (List<String>) already defines the generic type we need as String. So to simplify things and use type inference, we can re-write our method like this:

public void doSomething() {
    final List<String> names = new ArrayList<>();
                                         // ^^------ Inferred!
    ...
}

Both of these examples will end up generating the same bytecode, and both are syntactically valid. One of them is just a bit clearer and less noisy. If you write a lot of code with generics, you’ve probably gotten used to this quickly without feeling like you’ve lost something you needed. I know when I open code that was written pre-Java-7, I immediately have my IDE clean this up and move to the diamond operator.

What is Local Variable Type Inference?

Now that we know a little bit about type inference, let’s talk about what was added in Java 10. Simply put, the ability to infer types for variables declared in methods (“local variables”).

Take a look at this code where I’ve left out a critical type declaration:

public void doSomething() {
    final ??? name = "Todd";
}

Quick! What type is name? Obviously, it’s a String. Given that it’s pretty obvious what the type is, Local Variable Type Inference allows us to do what we did for the diamond operator above - use contextual clues to make the compiler figure this out for us! To do this, Java has introduced the var keyword to be used in this situation:

public void doSomething() {
    final var name = "Todd";   // name is inferred as a String!
}

And like the diamond operator, we could just as easily write this the “old way”, and be explicit about the types:

public void doSomething() {
    final String name = "Todd";   // name is explicitly a String!
}

Do you have to use var? No, just like diamond operator, you don’t. The “old way” is still perfectly valid and in some cases probably preferred. I write a lot of Kotlin and type inference is used quite frequently and even still, I define types explicitly for my own documentation when I feel I might not immediately figure out the types when coming back to the code in the future.

It should be noted that I’m not limited to variables declared within functions. We can use this with enhanced and indexed for loops as well:

final List<String> names = new ArrayList<>();

public void doSomething() {
    for(var name: names) {
        System.out.println("Name: " + name);
    }
    for(var i = 0; i < names.size(); i++) {
        System.out.println("Name: " + names.get(i));
    }
}

Isn’t This Dangerous?

What if we return that name? What if people infer class-level variables that others might depend on? Doesn’t this break everything?

Short answer: no.

The fact is, Local Variable Type Inference is limited to, well, Local Variables because they are scope-limited to the method (or loop declaration) they exist in. That means that people aren’t writing code that depends on those types, except the author of the method declaring them. The designers of this feature were explicit in their intent: this is not something that can be used in a situation where people consuming code with type inference are going to break. All method parameters and return types, all class fields, and anywhere else you define types in Java must still be explicitly declared.

This means if we come along one day and want one of the var local variables to be a different type (say, moving from an int to a double), no consumers of our code are going to break if our code continues to compile.

Boo! JavaScriptization of Java is Bad!

OK, this is where I feel people are confused. There are some languages where you don’t have to define the types of, well… anything, and let the compiler or interpreter figure it out for you. Take JavasSript for instance:

var x = "Todd"

Simple, right? Our x variable is a String. But nothing is what it seems because I can just redefine that if I want.

var x = "Todd"
x = 42  // Now it's an int?!

I think this is what gets people confused - they see the keyword var and they assume all bets are off and types no longer matter. This just simply isn’t the case with Java. Consider the Java version of this JavaScript code, it just won’t compile:

public void doSomething() {
    var x = "Todd";
    x = 42;    // Compiler fails on this line: 
               // Error: java: incompatible types: int cannot be converted to java.lang.String
}

See? We’re still safe. By introducing var, Java is not moving to a dynamic type system where the types of our variables can change just because we assign something new to them.

But I Already Know All This!

One of the things that truly bothers me about this new syntax is how some experts in our industry treat people who don’t immediately grasp it. People see this syntax for the first time, assume Java is becoming more like JavaScript because they see the var keyword, and other people who should know better make fun of them for their ignorance. Ignorance is not a crime. Bullying the ignorant makes our language look inhospitable to new people, and it just doesn’t need to be that way. I guarantee there are parts of Java that these experts don’t yet understand, and that’s OK. We’ve all been there.

I resisted the urge to link to the many, many screenshots I have from Twitter and Reddit illustrating this. Better yourselves and the people around you by explaining concepts when others are having trouble.

TL;DR:

  1. Var is your friend.
  2. Java var != Javascript var.
  3. Var saves you the trouble of explicitly specifying the types, but they still exist.
  4. Your var types are the same as if you explicitly declared them.
  5. Your var types aren’t going to leak out of your method and pollute other code.
  6. Help each other, bullying the ignorant isn’t productive.