Type Inference in Java (JEP 286) can be disastrous

Oracle is thinking of introducing Type Inference for Local variables in Java via the JEP 286 proposal.

Where Type Inference works well

One place where type inference does work well is on call to constructors. Eg -

 var list = new ArrayList<String>();

Here the right hand side gives full information about the type of the variable. The reader doesn't need to look elsewhere to find the variable type.

Where Type Inference is terrible

Type inference causes issues when placed on the return value of method calls. Take this example of a piece of code by someone who is not entirely familiar with the Java 8 Streams API:

void foo(List<String> list)  
{
    var newList = list.stream().map(s -> "Hello " + s);
    System.out.println(newList);
}

Here the author of the code doesn't know that he needs to call .collect(toList()) after the call to map(). He expects the type of newList to be List but it is actually Stream. Given an input list of ["world"], he expects the output to be:

[Hello world]

but instead the output turns out to be something like this:

[email protected]  

Here the error won't be known until runtime, and even then would cause a lot of head scratching until the author stepped through the code in the debugger to discover the actual type.

While the above example sounds trivial, here is a modified version with more dire consequences:

void foo(List<String> list)  
{
    var newList = list.stream().map(s -> "Hello " + s);
    var body = newList.toString()
    Http.post('http://myservice", body);
}

Here the type is converted to string and passed to the body of a REST API call. Now the problem is even tougher to track down. Not only is it not known till runtime, someone might have to look at the actual Http request to track down the root cause since on cursory examination the code looks fine.

Simply stating the type of the local variable in both instances would have reduced errors and made the code much easier to fully understand.

Explicit typing is good

Type inference in Java has become a popular idea due to widespread usage of weakly typed languages like Javascript. However, the popularity of TypeScript shows these same people are learning the value of types. In TypeScript, type inference is a necessity due to introduction of types in the first place and general lack of type definitions, in Java its not.

Languages like Scala have tried to jump on the same implicit/inferred type bandwagon, but eventually failed.

  1. Without explicit types, one has to constantly look at the signatures of method calls to see the type of the value being assigned to a variable. Not only is this cumbersome, its almost impossible when doing code reviews in places like Github where you don't have a full IDE to easily jump to method definitions.

  2. Implicit typing leads to incorrect understanding of code that can lead to bugs that are not known till runtime. The code in previous section is an example of that.

  3. Users of C/C++ had to adopt hungarian notation to encode the variable types in variable names since heavy use of macros in those languages obscured the actual type of the variables.

  4. IDEs like Jetbrains and Eclipse auto complete the variable types anyway so there is no extra typing effort to write those types.

Conclusion

Implicit types only results in removal of information, with nothing much to gain. Especially considering the fact that IDEs autocomplete and refactor the type names anyway.

For Java, removing the need for explicit types would only be a step backward that results in more error prone code that is hard to fully understand and reason about.

Oracle should limit its scope of JEP 286 to just local variables with constructor calls, as shown above. That is the only place where type inference brings an actual advantage without leading to loss of information.

Show Comments