piątek, 17 listopada 2017

JEP 286 - a case of language flexibility

Introduction


As a part of the future Java 10 specification, Oracle has proposed JEP 286 - Local Variable Type Inference. Full specification can be found here: http://openjdk.java.net/jeps/286 Since publication, it has raised a great deal of controversy among Java devs around the world. What is this all about then?

Local variable type - VAR


The proposal currently boils down to introduction of a new 'reserved type name' - var - that could be used instead of a variable type. Actual type would be then inferred by the compiler. Therefore you would be able to write:
var list = new ArrayList();
instead of:
List list = new ArrayList(); 

It should remind you of a Java 7 feature called diamond operator which was designed to eliminate verbosity of a RHS (right side assignment). Since then, Java compiler is able to infer generic construction type by looking at the variable type, for example:
List list = new ArrayList<>();
Var on the other hand aims to eliminate verbosity on the LHS (left hand assignment). Also, due to the fact that var would not be a keyword, the change is totally backward compatible, making following compilable:
var var = "var"; // ;-) 

The good, the bad (and the ugly)


So, is this good or bad? The answer is the usual one - it depends :-D

There are obvious advantages on var. The biggest one is already mentioned decrease of (unnecessary) verbosity. Does is make sense to write: 
int counter = 10 or MyOwnService service = new MyOwnService() ? 
Of course it does not, as type is apparent for writer and will be for consecutive readers of the code. However, it is easy to realize that the same feature is an obvious disadvantage in some contexts, like: 
var service = MyServiceFactory.forUserData(data)
 or even worse (as nothing is implied by context):
var result = service.calculate();
The fact that with var a result of an operation will not have an apparent type (for devloper that is) can cause even more trouble, especially in a context when result is later used in a generic way (like string conversion). Just take a look at the following, compilable code:
var ids = users.stream().map(user -> user.getId()); log.info("User ids: {}", ids);
The result in runtime will be completely unexpected, as instead of user ID list, stream's object reference will be logged. Why? Because the developer forgot (or didn't know) that the stream needs to be collected to list first. If the code had explicit typing like:
List ids = users.stream().map(user -> user.getId());
the code would simply not compile (as the compiler would know that the type expected by the developer was different than RHS). I often refactor, often work with legacy code and believe that the example is not an artificial one.

Also, taking into account the diamond operator, with var the developer will need to decide which "sugar" should be used in a particular context, either:
var list = new ArrayList();
or
List = new ArrayList<>(); 

Make peace, not VAR ?


To sum up, var has some benefits but also brings some dangers. From my perspective it's a typical case of added language flexibility, that can make life a bit easier but on the other hand can cause harm if used improperly or without caution (aka a monkey with a razor). Similar concept has been introduced into C# and developers can live with that. On the other hand, Microsoft in the official code convention guide (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) explicitely warns developers not to use var in a number of contexts. 

Taking into account a typical life of a regular developer, that is often chased by deadlines, get distracted while writing the code or simply has not enough coffee ;-) I believe that var should be limited to cases when a type is apparent (local variable and constructor call for example). As the code is read more than written, it's better to have a code that is easier to understand than shorter to write. 

Brak komentarzy: