My colleague Joe Darcy has recently posted the Early Draft Review for JSR 334, Project Coin. (Joe is the spec lead for this JSR.) Let’s see what the EDR has to say about the diamond operator. First, there are some changes to the grammar that allow an empty type argument list. Not too surprising. Then, it continues:

Let K be the class type being instantiated. To create an instance of K, i, a constructor of C is chosen at compile-time by the following rules, where C is chosen as follows: If the type denoted by K has an empty type argument list (diamond operator) and K is a classtype of the kind G<X1, X2… Xn>, then C is a type constructed from K, where all constructors c1(), c2() … cn() are obtained from corresponding constructors in G by prepending additional type-arguments T1, T2 … Tn, where the declared bounds of T1, T2 … Tn are obtained from the declared bounds of X1, X2 … Xn, where all occurrences of X1, X2… Xn are replaced by T1, T2 … Tn. Furthermore, all occurrences of types X1, X2 … Xn in the obtained constructor signatures are replaced by T1, T2 … Tn.

otherwise C == K

Uhhm, I’m not entirely sure, but I think this means that the compiler just does the right thing. 🙂

I should mention that, just above this, the draft states:

Note to readers: the description of diamond semantics below will be reworked in future drafts of the specification.

Whew! I know that sometimes specifications are opaque — and I’ve written some pretty opaque specifications in my day — but this one really takes the cake. Fortunately, this is only a draft, so this can get fixed before the final version of the specification.

But what does this mean, really? Instead of trying to explain the draft specification, I’ll instead pursue a more intuitive approach. Basically, here is what happens, as I understand things. When you use the diamond operator, the compiler will infer from context a set of type arguments that make the expression work. This is a only little less abstract, so let’s look at some examples.

List<Number> list = new ArrayList<>();

What type argument does the compiler infer for the ArrayList? The only argument that will work is Number, so that’s what will get used. No other type will work in the diamond, not even a subclass of Number, such as Integer, since the resulting type ArrayList<Integer> would have no type relationship with List<Number>. (For further explanation, see Angelika Langer’s Generics FAQ #102, or Josh Bloch’s Effective Java, Second Edition, Item 25.)

In this case the context is pretty clear. In what other contexts can one use the diamond operator?

As I mentioned in my previous post, what I’ve done is to run a Jackpot-based diamond converter over a bunch of source files in the JDK. This applies the diamond operator everywhere it can possibly be used. It identified several hundred potential use sites for diamond. These cases broke down as follows:

field or local variable initializer (61%) right-hand side of assignment statement (33%) method argument (4%) return statement (2%)

There were also a tiny number of obscure edge cases that I’ll ignore for now. Let’s look at each of the four major cases in turn.

1. Field or Local Variable Initializer

This case is by far the most common. This is what’s shown in my one-line example above. It’s also probably the most common usage cited in toy examples given in talks or articles about Coin and the diamond operator. At least, in Joe’s talks at JavaOne and Devoxx last year, the one-liner examples he showed were all field or local variable initializers. Here’s an example of an actual change I pushed recently, to src/share/classes/java/lang/reflect/Proxy.java. The original code was:

private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache = new WeakHashMap<ClassLoader, Map<List<String>, Object>>();

and the replacement code is:

private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache = new WeakHashMap<>();

Now even though this is a pretty complex generic type, figuring out what gets inferred inside the diamond is pretty easy. It’s right there in the declaration on the left-hand side. Having the type arguments repeated on the right-hand side is noisy and redundant.

2. Right-hand Side of Assignment Statement

Consider the following statement:

perms = new ArrayList<>();

What does this do? Well, you have to look for the declaration of the “perms” variable in order to figure it out, but that’s not too hard. In decent code it should be obvious anyway. In this case (src/share/classes/java/io/FilePermission.java) the declaration is

List<Permission> perms;

so this case is also pretty straightforward. It’s true that you can’t tell from the assignment statement by itself what the instantiated type will be. But in order to use the perms variable, you have to know what type it is anyway (or you have to ask your IDE) so this isn’t too much of a burden.

3. Method Argument

Here’s an example from src/share/classes/com/sun/java/util/jar/pack/BandStructure.java, around line 1700:

List<List<Attribute.Layout>> attrDefs = ...; ... attrDefs.set(i, new ArrayList<>(...));

What gets filled into the diamond here? This is where it gets a bit tricky. Looking at the snippets above, it’s pretty obvious that the type argument has to be Attribute.Layout. And indeed it is. But the compiler has to go through a couple different steps to get here.

The “set” method of attrDefs takes parameters (int, E). The attrDefs variable is declared List<E> where E is List<Attribute.Layout>. ArrayList<> needs to match List<Attribute.Layout>; therefore, what goes in the diamond is Attribute.Layout.

This isn’t exactly the analysis the compiler goes through, but you can see this case has many more moving parts than the others. This is actually a pretty simple case of a method argument as well: we didn’t have to do method overload resolution. If the set() method had been overloaded, the compiler would use the types of the arguments to figure out which method to use. But with diamond we’re trying to infer the argument type based on the type the method accepts… from what I understand this isn’t actually a circularity, but it greatly complicates the compiler’s analysis.

4. Return Statement

Here’s an example from src/share/classes/java/lang/ClassLoader.java:

public Enumeration<URL> getResources(String name) throws IOException { ... return new CompoundEnumeration<>(tmp); }

This isn’t too different from the assignment statement. The type required is determined based on the return type of the method, and in this case it’s URL.

Summary

The four cases described here account for virtually all of the cases where the diamond operator can be applied in real code. Of these four cases, one case — the initializer case — is the most common in real code and also the most common in toy examples and in slideware. This is a good validation of the designs that have been presented over the past couple years. How many times have we seen toy examples used to justify features that turn to be hardly useful in practice?

The two most common cases (initializers and assignment statements) comprise over 90% of actual uses found of the diamond operator. Furthermore, their type inference is straightforward and they’re fairly easy for programmers to understand. Other cases may involve much more complex type inference, and are potentially harder to understand (hm, maybe there’s a correlation there), but they also occur much less frequently.