Diamond operator and anonymous classes
Derek Foster
vapor1 at teleport.com
Thu Jun 23 18:56:30 PDT 2011
Hi, folks.
I've been experimenting with applying the JDK 7 language additions to some code bases of mine, and anticipating such changes to my employer's code base at such time as we can upgrade to JDK 7. In most cases, this has gone quite well. However, I have run into one issue that has been rather annoying in some places. This has to do with the diamond operator.
As originally proposed and reviewed on the Project Coin mailing list, the diamond operator could be used in both ordinary instance declarations, like:
private final List<Integer> foo = new ArrayList<>();
and in anonymous class declarations such as:
public static final Callable<Integer> TASK = new Callable<>() {
public Integer call() {
return doSomething();
}
};
However, a later decision (by the Expert Group? or someone else?) declared that there were technical problems in implementing this for anonymous classes, and that support for using the diamond operator in anonymous classes would be dropped. (Unfortunately, I am unable to go review the expert group's discussions to determine what the detailed motivations were for making this change, since the expert group's discussions are apparently not public. I am a bit frustrated by the lack of visibility for this change and its motivations.)
I originally thought that restricting use of the diamond operator for anonymous classes would be a minor, rare annoyance. This issue was in fact one of the first issues that I encountered when trying to move an existing codebase to use JDK 7 constructs. (I told IntelliJ Idea to globally convert all diamondable instances of code in my project to use the diamond operator. It did so, and then my code wouldn't compile. I then found that diamond was disallowed for anonymous classes, informed JetBrains, and they fixed Idea not to suggest an autofix in those cases.)
In that case, a very small project, I encountered problems with this kind of code, which I would have liked to use the diamond operator for but couldn't:
class MyNiftyClass {
public static final Comparator<MyNiftyClass> FORWARD = new Comparator<MyNiftyClass>() {
@Override
public int compare(MyNiftyClass first, MyNiftyClass second) {
... comparator logic goes here ...
}
};
public static final Comparator<MyNiftyClass> BACKWARD = new Comparator<MyNiftyClass>() {
@Override
public int compare(MyNiftyClass first, MyNiftyClass second) {
return FORWARD.compare(second, first);
}
};
...
}
In my employer's codebase, however, I am encountering MANY more instances of this problem, due to the architecture of the framework it is using. Typical (paraphrased) code looks like this:
// One member of a large hierarchy of items that have a similar structure
class NativeColorWrapper extends NativeObject {
// A public factory for creating instances of NativeColorWrapper by wrapping a native
// pointer. Every subtype of NativeObject has one of these, named FACTORY, which
// is used for all instance creation.
public static final NativeFactory<INativeUnboundContext, NativeColorWrapper> FACTORY = new NativeFactory<INativeUnboundContext, NativeColorWrapper>() {
@Override
public NativeColorWrapper create(INativeUnboundContext context, long nativePointer) {
return new NativeColorWrapper(context, nativePointer);
}
};
// An internal cache, in this case for lazily binding identifiers to sorted lists of items.
// There are multiple instances of these caches, with various differing generic
// parameter types depending on the data to be cached.
private final LazyMap<NativeIdentifier, SortedSet<NativeItem>> _itemCache = new LazyMap<NativeIdentifier, SortedSet<NativeItem>>() {
protected SortedSet<NativeItem> fetch(NativeIdentifier binding) {
long[] nativePointers = someNativeMethod(binding.toString());
return new TreeSet<NativeItem>(wrapItems(NativeItem.FACTORY, nativePointers));
}
};
...
}
A few months ago, Joe Darcy posted a message on the Project Coin mailing list suggesting (if I understood it correctly) that the reason for dropping support for anonymous classes was due to problems with potential use of non-denotatable types as generic type parameters, which would require a change to the class file format if it were to be supported.
This is backed up to some degree by bug reports such as this one:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6894753
Which states:
"
In all failing cases we have that the parameter type for such instantiated type contains an intersection type. Since there's no AST representation for intersection types, javac fails to create an AST node for the inferred type, and this result in the crashes described in this report.
There are two ways for fixing this problem. One is to add AST internal support for intersection types - so that we can keep javac happy. Another is to change the attribution scheme for inner classes (but I don't see this as a feasible approach).
"
It seems to me that the current "solution" of completely disallowing use of diamond with anonymous classes is an overreaction to the problem that has so far been described. It disallows problematic constructs, but it also disallows non-problematic, useful constructs. I would like to suggest a third option from the two that are listed in the above bug report:
The diamond operator would be allowed for anonymous classes if and only if none of the generic type parameters which are inferred would be non-denotable types, and would be treated as a compile-time error otherwise. ("Error: Can't use diamond operator with anonymous class when non-denotable types would be inferred.")
Use of the diamond operator would help readability quite a bit in the examples I have shown above. Many of the declarations could fit on one line, while they currently cannot. I am not aware of any technical reason why this couldn't be allowed (although I'm sure some list participant will swiftly inform me otherwise if I am wrong. :-) ) It appears to me that allowing the diamond operator for anonymous classes with only denotatable types would allow me to use the diamond operator in all of the cases that I actually care about, while still avoiding changes to the class file format and the problems suggested in the above bug report.
Could something like this be put into JDK 8? (I am assuming it's too late for JDK 7, but I would be thrilled to be wrong about this.)
Thanks for any information.
Derek
More information about the coin-dev
mailing list