With Java 10 a new feature was introduced to the language: local variable type inference. In this short post, I am going to take a closer look at what this brings us in the Java world.
Small disclaimer: lately most production code I have produced was written in Kotlin, which means I have gotten used to good type inference embedded in a language. Of course, the concept of type inference is nothing new really, many popular languages include support for it. With Java 10 support has finally come to Java. Is it any good?
A few examples
To get started, here’s a short example of the contrast between pre-Java 10 variable declarations and the new
If we investigate this, two things stand out immediately. Firstly, it’s not all that more compact, for some reason I expected more of this. What does significantly improve is that we are no longer repeating ourselves in terms of typing, changing the type of both declarations is slightly less work when using
So, another example, using collections with generics:
In this particular case the code is not significantly more compact than the
var equivalent. It is when we compare it with the pre-Java7 syntax, but since Java7 gave us diamond operators which we can omit completely we don’t have to repeat our generic types. This scenario has the same benefits as we’ve seen before; changing the type for the declaration is slightly less work when using
Let’s go a little deeper, and look at this example, which is in a similar vein to the one we’ve seen before:
This shows something that happens when working with
var. Because the type is inferred, we end up having to cast to
List if we want to control the type of the declared variable which is
ArrayList if we don’t cast. In all fairness, for local methods, you can argue there’s not a lot of value in working against interfaces. This is just something to consider.
var can also be of use in for loops:
Because Java can easily infer the type it is iterating over, we no longer have to specify the type explicitly in the loop declaration itself. This can help save a bit of time because when the collection type needs to change, you won’t need to change the loop declaration too.
What we can’t do with var
Aside from the examples listed above, there are a few things we can’t do with
We can’t use
var anywhere other than when declaring local variables. I would have loved to see this feature more widely available, somehow the current implementation makes the language feel inconsistent, a bit unbalanced. Also, we are required to initialize it with a value which type can be inferred, which means leaving the variable uninitialized or
null is not an option. There’s a workaround for this last issue, we can cast the
null like so:
(String)null to explicitly provide type information.
Inferred types from method calls
As with any feature added to a language, there’s a chance to use it in a contexts where maybe you shouldn’t. This, for example, is one of these occasions where using
var won’t lead to code that’s more readable:
Just assume the method is a member function of a different class. The inferred type won’t be obvious from the declaration, which means you can’t deduce from the
var variable = returnValue(); which type
variable will be. IDE’s can help out a lot there, but maybe this is not the code you’d want to write. Every downside has its upside though, which means changing the type returned from the
returnValue() method won’t have any effect on the
var declaration. Which approach you prefer is up to you.
In all fairness I have to add to this that this is a common problem in languages with type inference; this is not an issue specific to the Java implementatiom.
I’m a bit on the fence on this one. Having been spoiled with a good
var, val implementation in Kotlin which is consistently available in any scope, I can’t help but feel a bit underwhelmed by the value
var has for Java. Sure, refactorings may be a bit less work, and it could save some time in typing while writing code, but that is about it. We can’t use the functionality in lambda’s – although this will change in java 11 with JEP-323 -, fields, or anywhere else outside of a local scope for that matter.
This touches on the main issue I have with this feature — it breaks with the consistency of the language. While writing this post I wrote some Java code to play with it, and I found it distracting to use
var and standard declarations intermixed with each other. Maybe future versions of Java will see a more broad implementation of
var so this will no longer be an issue. Whether or not this actually is an issue is up to the you, of course.
Another thing that I would have loved to see is immutable
var as its own option in Java 10. Rather than having to define
final var someVariable, it would feel more compact and arguably more clean if you could specify
val someVariable. Again, I can see why the design turned out different – within the Java language use of final is explicit, departing with that concept just for local variable type inference would have a negative effect on the consistency of the language.
Then again, if consistency is a primary goal, allowing
var in limited contexts isn’t really consistent either.
It’s good to see new features making their way into Java. Despite its age and promise to remain backwards compatible, Java is still moving forward. When it comes to the addition of local variable type inference though, I fear that my feelings are best expressed by ¯\_(ツ)_/¯. I guess Kotlin has spoiled me a bit 🙂
As with many new technological developments, there are many resources available online covering topics like this. Stuart Marks and Simon Maple created this cheat sheet to help you figure out how to deal with local variable type inference in your projects.