In a previous post I listed the features added to Java 10. Naturally in a six-month update, the list isn’t long and most of them are updates to the runtime (such as very welcome garbage collector updates or internal changes).
But the number 1 headline grabber is the addition of type inference for local variables, and it’s a biggie. Let’s take it for spin.
What is type inference?
You’re all familiar with the following ritual:
Customer customer = new Customer("Alan");
Which, if you think about it, is a bit absurd. We have to tell the compiler twice what type of object we’re creating. There is a reasoning behind this – technically the left hand side is telling the compiler what type we want the reference to be stored as, whilst the right hand side is the concrete object we’re creating. Sometimes, the difference is important and relevant – when programming to interfaces.
Example:
AbstractCustomer customer = new CreditCustomer("Alan");
Here want to instantiate a specific type of customer, but for the rest of the code, we only want to call the methods that are common to all customers. Polymorphism, blah blah blah. We’re not interested in that today, I just wanted to point out there is a reason for the left-hand-side-right-hand-side apparent duplication.
Here’s the thing – for the most part the two sides of the equation are duplicated. I want a customer and it needs to be of type customer.
Don’t Repeat Yourself is a bad thing and it’s almost like traditional Java wants us to repeat ourselves!
Anyway, stop waffling and cut to the chase. From Java 10 onwards, we can now let the compiler guess – or infer the type that we want…
var customer = new Customer("Alan");
“var” is one of those rare things in Java, a brand new keyword! Java will now guess (it’s not hard is it?) that the type we want is Customer.
That’s it.
For the rest of the post I’ll describe a more realistic use of var. I’m picking an example from Spark, but don’t worry if you haven’t used Spark before, the example is actually generic.
A Real World Example
As usual with these toy examples, things don’t seem very groundbreaking but I believe this feature will have a major and positive impact on code productivity. Consider this monster from my Spark Training Course:
result = totals.mapToPair(tuple -> new Tuple2<Long, String> (tuple._2, tuple._1 ));
Now, I should know what the return type from this method call is. Erm, well, I should but it’s a bit of a headache. I know that the return class is some kind of “RDD”, I think a JavaRDD. And I can guess from the <Long, String> that the resulting return type will also have the same generic. So, I nervously type into the IDE:
JavaRDD<Long, String> result = totals.mapToPair(tuple -> new Tuple2<Long, String> (tuple._2, tuple._1 ));
Ouch. No! I now have a compile error…the type on the Left Hand Side is wrong. So now we reach a ludicrous situation: the compiler knows what the type should be, and I don’t. And the compiler won’t let me proceed until I guess correctly. (Ok, what I mean is – until I work out the right type).
Often on the Spark course I advise the viewer to simply leave off the return type and then use Quick Fix for the IDE to add in what it thinks is the right type!
Using the “Create Local Variable” Quick Fix should add in the left-hand-side declaration automatically.
I only advise using Quick Fixes if you know what you’re doing – in this case I’m basically asking Eclipse to do a type inference for me. But it’s very hit and miss – in this case, it gets it wrong. Or sort of half right:
For some reason (I care not why) the generics on the left hand side haven’t been inferred so the compiler is still moaning. A second “quick” fix will rectify this…
And – hooray, we finally have the right answer – it seems I had forgotten that the type was actually “JavaPairRDD”:
JavaPairRDD<Long, String> result = totals.mapToPair(tuple -> new Tuple2<Long, String> (tuple._2, tuple._1 ));
But what a performance! And obviously the compiler knew the right answer all along. It was just teasing us.
So, to the point – when working in real coding situations like this, it can be easy to be unsure of the correct type you need for the declaration – and actually knowing the right type isn’t particularly illuminating. So, in Java 10 I could have avoided all that mess and jumped straight to:
var result = totals.mapToPair(tuple -> new Tuple2<Long, String> (tuple._2, tuple._1 ));
Much cleaner and I hope in the long run, simpler. Note that none of this destroys strong typing – the type of result is still the same as it was, and we can only call the methods defined in the JavaPairRDD class, exactly as before.
I hope these kind of modern features are going to come thick and fast to future Java versions and that it will prove a compelling reason to upgrade – only time will tell…
[A footnote that after a long discussion Java decided not to implement a var/val keyword pair as in Scala. val would define an immutable value – whilst valuable it was decided this would over-complicate type inference, and it is arguably not all that useful on local variables anyway. That’s a bit disappointing but it’s true that immutability is orthogonal to type inference. I hope that a future version of Java will introduce some strong language level support for immutability.]