Java Type Inference Won't Support Mutability Specification

| by Abraham Marín Pérez on Dec 22, 2016. Estimated reading time: 4 minutes |

Java type inference, a proposed Java feature that will allow developers to replace the explicit type declaration of variables with a var keyword, won't support a keyword to differentiate immutable from mutable variables, due to lack of consensus within the community regarding how this should be implemented, recent communications show. Some of the proposed choices for immutable variables included val and let. Also, to prevent a long debate about corner cases, a number of such cases will be ruled out for simplicity. Although the JEP doesn't indicate a target version, Java 10 seems likely.

After a series of proposals and consultations to fully define the scope of JEP 286, Brian Goetz, Java language architect at Oracle, noted that there was sufficient consensus to implement a new feature to infer the type of a local variable (and avoid the ceremony if explicitly indicating the type), and that such feature should use the word var. In addition to this, the community also highlighted their desire to differentiate between mutable and immutable inference in the same way that other languages like Scala, Kotlin or JavaScript do. However, while there was agreement on this being a useful feature, there wasn't agreement on how this differentiation should be implemented, with strong supporters and opposers to var/val, var/let, and (raw type)/var. To prevent this debate delaying the type inference aspect of things, the feature leaders decided to reduce the scope back to simply inferring type for local variables, leaving the differentiation of mutability out of the picture. Despite this, the type of immutable local variables will still be inferrable, albeit with the slightly longer construct final var.

var s = "hello"; // type of s is String
var keys = map.keySet(); // assuming map is of type Map<K, V>, type 
                         // inferred for keys will be Set<K>
final var MAX_COUNT = 100L; // MAX_COUNT will be immutable long

The update was also used to remind the extent of inferrability. On one side, only initialisation information will be used to infer the type of the variable; this means that variables that aren't initialised when declared will need explicit typing, although it will also help prevent some potentially obscure errors (like the wrong type being inferred for something that happens to the variable far down the code). On the other side, only local variables will be type-inferrable, excluding fields and methods, based on the assumption that these are part of the public interface of a class and therefore need to be explicitly defined by the programmer. Other cases where type inference won't work are initialisation expressions that imply some type guessing of their own like the following:

List<String> list = new LinkedList<>(); // type not indicated in
                                        // initialisation, but inferred
                                        // from variable declaration
var list = new LinkedList<>(); // error, impossible to infer a type for
                               // the contents of the list

Function<String, Integer> f = s -> s.length(); // type of s and length
                                               // inferred from
                                               // declaration
var f = s -> s.length(); // error, type of s unknown, return type of
                         // length unknown

int[] array = {1, 2, 3}; // 1, 2, 3 interpreted as integers
var array = {1, 2, 3}; // error, poly expressions not supported
                       // (see below)

// Use Integer.valueOf(int)
Function<Integer, Integer> intFunction = Integer::valueOf;

// Use Integer.valueOf(String)
Function<String, Integer> stringFunction = Integer::valueOf; 

// error, ambiguous initialisation
var function = Integer::valueOf; // unable to know which overloaded
                                 // version of valueOf should be used

It is unclear at this point whether some specific subcases of the above will be supported or not. As Goetz says, "we derive the type of the variable by treating the initializer as a standalone expression, and deriving its type. However, array initializers, like lambdas and method refs, are *poly expressions* [...] [s]o they are rejected". Poly expressions is a concept introduced in Java 8 with lambdas, and differ from ordinary expressions in the way their type is calculated. For ordinary expressions, the type can be obtained at compile time by checking the contents of the expression; poly expressions, on the other hand, need also the target type (i.e. the type of the variable the expression will be assigned to) to calculate the type. This means that poly expressions already imply some type inference of their own and, therefore, attempting to infer a type of a poly expression might be very hard or even impossible. However, there are some scenarios that fit into this category but seem to provide enough information to infer an appropriate type, and that might be considered for inclusion in the future:

var a = {1, 2, 3}; // could infer type int[]
var f = (String s) -> s.length(); // could infer type
                                  // Function<String, Integer>

Despite its limitations, it looks like local variable type inference can help shrink the gap between Java and other JVM languages, reducing boilerplate for Java programmers. And, in the same way that lambdas are now being expanded with additional functionality, it could happen that type inference is improved after this first version. This would confirm the unofficial dynamics of JVM languages acting as an experimentation ground for new features, with the most popular ones being eventually imported to Java.

Rate this Article


Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

weak by Steven Sagaert

looks pretty weak and hence useless compared to what's available in languages with "real" type inference

Re: weak by Jörg Buchberger

OTOH you can easily imagine type inference becoming a code smell - it's a bit like using Object for every variable - I see myself already debugging tons of problems introduced by juniors, that got entangled by their inferences

type inference != dynamic typing by Steven Sagaert

It’s not : using Object everywhere is the equivalent of dynamic typing. Type inference is static typing : you cannot change the type once a value has been assigned & it’s all checked at compile time, not runtime.

Type inference is not just more work for the compiler... by Javier Paniza

because the programmer brain now has to do type inference too.

Yes, type inference may save some typing writing the code, but the programmers expend more time reading code than writing code. So now, when I read:

var x = m;

My brain have to do the type inference to figure out what in the hell is that x, specially if that is not my code. However, if I read:

String x = m;

I now that x is String without thinking.

Yes. It's true that type inference can create more elegant code in some cases, but many programmer likes to try new things, just because they are cool, so we'll have a type inference abuse for sure, with a lot of illegible code.

Don't turn Java in JavaScript. Don't make me think.

Re: Type inference is not just more work for the compiler... by Gareth Rowlands

Don't worry too much about type inference making Java code harder to read. There's ample evidence from other programming languages with the same feature that it's a win. Java's inference is very limited, so it would be hard to abuse it. Oracle are still really conservative with language additions, adding only features that are well proven elsewhere.

Making Java code shorter and eliminating repetition is likely to make it easier to read, not harder.

Re: Type inference is not just more work for the compiler... by Jonathan Allen

If you don't want to think, type inference is your friend. In order to write "String x = m", you first need to know what "m" is.

Conversely, you generally don't need to know what "x" is. The vast majority of the time what's important is how it is being used in business terms, not its compiler type. And of course its list of methods, which you get from typing "x." even if you know that x is a string.

I've been using type inference in C# for over half a decade and only on very rare occasions have I found it to make code harder to read. And even then, it was when dealing with numeric typed where I needed to know if it was an int, double, or decimal to verify correctness.

Re: weak by Abraham Marin-Perez

New features in Java rarely evolve as fast as in other, newer languages, usually because the other languages don't have as much baggage to carry. The promise of backwards compatibility is surely a weight that slows progress down, but it's also one of the defining features of Java and the main reason many teams and companies chose it.

However, it's fair to point out that some of the limitations in Java type inference is by design: the authors of the JEP indicated that fields and methods are meant to be the interface of a class, and therefore their type should be explicitly defined rather than implicitly inferred. This means that, technical issues aside, type inference in Java is unlikely to go as far as in other languages.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

7 Discuss