Diamond operator and anonymous classes

Dan Smith daniel.smith at oracle.com
Fri Jun 24 09:02:47 PDT 2011


The fundamental problem is that inference in this case is more than just inference of type arguments for a constructor call -- these are type arguments for an 'extends' clause, which impacts inheritance/overloading and the generated class file.

Motivating the deferral is that there were a few different ideas about how to handle this, and we wanted to give it some time, perhaps incorporating changes from Project Lambda in SE 8, before committing to something (there's a lot of overlap between anonymous classes and lambda expressions).  Some of these ideas:

- Perhaps we will want to expand the set of denotable types.

- There may be other reasons in Lambda to infer type arguments that are restricted to a denotable subset of types, and we'll want one cohesive approach for this.

- With lambda expressions, we infer type arguments from the expression's method signature, not just its context.  Perhaps we could do something similar with anonymous classes.

So why not do something simple now and then tweak it later?  Because inference changes (even when they're "improvements") are typically source incompatible.  Once we've committed to a particular strategy, it is hard to change it later.

Also, Remi's answer is worth repeating: most of these use cases (though not all of yours -- LazyMap appears to be a class, for example) will probably be re-expressible as lambda expressions in SE 8.  In that case, we'll infer not just the type arguments, but the interface name and the method signature as well.  And give you better performance to boot.

—Dan

On Jun 23, 2011, at 6:56 PM, Derek Foster wrote:

> 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