Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Java Type Inference Won't Support Mutability Specification

Java Type Inference Won't Support Mutability Specification

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