Should local variable type inference be added to Java? This is the question being pondered right now by the Java language team.
Local Variable Type Inference
JEP-286 proposes to add inference to local variables using a new psuedo-keyword (treated as a "reserved type name").
We seek to improve the developer experience by reducing the ceremony associated with writing Java code, while maintaining Java's commitment to static type safety, by allowing developers to elide the often-unnecessary manifest declaration of local variable types.
A number of possible keywords have been suggested:
- var - for mutable local variables
- val - for final (immutable) local variables
- let - for final (immutable) local variables
- auto - well lets ignore that one shall we...
Given the implementation strategy, it appears that the current final
keyword will still be accepted in front of all of the options, and thus all of these would be final (immutable) variables:
- final var - changes the mutable local variable to be final
- final val - redundant additional modifier
- final let - redundant additional modifier
Thus, the choice appears to be to add one of these combinations to Java:
- var and final var
- var and val - but final var and final val also valid
- var and let - but final var and final let also valid
In broad terms, I am unexcited by this feature and unconvinced it actually makes Java better. While IDEs can mitigate the loss of type information when coding, I expect some code reviews to be significantly harder (as they are done outside IDEs). It should also be noted that the C# coding standards warn against excessive use of this feature:
Do not use var when the type is not apparent from the right side of the assignment.
Do not rely on the variable name to specify the type of the variable. It might not be correct.
Having said the above, I suspect there is very little chance of stopping this feature. The rest of this blog post focuses on choosing the right option for Java
Best option for Java
When this feature was announced, aficionados of Scala and Kotlin naturally started arguing for var and val. However, while precedence in other languages is good to examine, it does not necessarily apply that it is the best option for Java.
The primary reason why the best option for Java might be different is history. Scala and Kotlin had this feature from the start, Java has not. I'd like to show why I think val or let is wrong for Java, because of that history.
Consider the following code:
public double parSpread(SwapLeg leg) { Currency ccyLeg = leg.getCurrency(); Money convertedPv = presentValue(swap, ccyLeg); double pvbp = legPricer.pvbp(leg, provider); return -convertedPv.getAmount() / pvbp; }
Local variable type inference would apply fine to it. But lets say that the type on one line was unclear, so we choose to keep it to add clarity (as per the C# guidelines):
public double parSpread(SwapLeg leg) { val ccyLeg = leg.getCurrency(); Money convertedPv = presentValue(swap, ccyLeg); val pvbp = legPricer.pvbp(leg, provider); return -convertedPv.getAmount() / pvbp; }
Fine, you might say. But what if the code is written in a team that insists on marking every local variable as final.
public double parSpread(final SwapLeg leg) { val ccyLeg = leg.getCurrency(); final Money convertedPv = presentValue(swap, ccyLeg); val pvbp = legPricer.pvbp(leg, provider); return -convertedPv.getAmount() / pvbp; }
Suddenly, we have a mess. Some parts of the code use final to indicate a "final" (immutable) local variable. Whereas other parts of the code use val. It is the mixture that is all wrong, and it is that mixture that you do not get in Scala or Kotlin.
(Perhaps you don't code using final on every local variable? I know I don't. But I do know that it is a reasonably common coding standard, designed to add safety to the code and reduce bugs.)
Contrast the above to the alternative:
public double parSpread(final SwapLeg leg) { final var ccyLeg = leg.getCurrency(); final Money convertedPv = presentValue(swap, ccyLeg); final var pvbp = legPricer.pvbp(leg, provider); return -convertedPv.getAmount() / pvbp; }
This is a lot more consistent within Java. final continues to be the mechanism used everywhere to get a final (immutable) variable. And if you, like me, don't worry about the final keyword, it reduces to this:
public double parSpread(final SwapLeg leg) { var ccyLeg = leg.getCurrency(); Money convertedPv = presentValue(swap, ccyLeg); var pvbp = legPricer.pvbp(leg, provider); return -convertedPv.getAmount() / pvbp; }
I understand the objections many readers will be having right now - that there should be two new "keywords", one for mutable and one for immutable local variables and that both should be of the same length/weight (or that the mutable one should be longer) to push people to use the immutable form more widely.
But in Java it really isn't that simple. We've had the final keyword for many years. Ignoring it results in an unpleasant and inconsistent mess.
Summary
I don't personally like local variable type inference at all. But if we are to have it, we have to make it fit well within the existing language.
I argue that val or let simply does not fit Java, because final already exists and has a clear meaning in that space. While not ideal, I must argue for var and final var, as the only combination on offer that meets the key criteria of fitting the existing language.