Diamond operator and anonymous classes

Derek Foster vapor1 at teleport.com
Fri Jun 24 23:22:22 PDT 2011


Hi, Rémi.

Thanks for your reply.

I've been reading the traffic on the lambda-dev list since its beginning, so yes, I am aware that the specific examples I listed can be rewritten as lambdas. However, that doesn't address the general case. For instance, although the specific example that I gave of a factory class happens to have a simple one-liner constructor call as the method body, that's not necessarily true in the general case of the code I am dealing with. Some of the factories are moderately complex. If, for instance, an anonymous factory instance needs to override more than one method from its base class (which is an abstract class, by the way, not an interface) in order to accomplish its task, or needs to declare other worker methods to be called from its main factory method, a lambda won't suffice. Similarly for the LazyMap abstract class. I will grant that I have a hard time imagining an instance of Comparable which couldn't easily be a lambda, but I'm sure they exist in someone's code.

So, you are right that lambdas would be an attractive option in much of this code, but they aren't sufficient to solve all of the issues I am raising.

Derek

-----Original Message-----
>From: Rémi Forax <forax at univ-mlv.fr>
>Sent: Jun 24, 2011 1:19 AM
>To: coin-dev at openjdk.java.net
>Subject: Re: Diamond operator and anonymous classes
>
>Your examples are lambdas, Java 8 will introduce a syntax for that.
>By example,
>
>public static final Comparator<MyNiftyClass>  BACKWARD = new Comparator<MyNiftyClass>() {
>     @Override
>     public int compare(MyNiftyClass first, MyNiftyClass second) {
>         return FORWARD.compare(second, first);
>     }
>};
>
>can be rewritten it to:
>
>public static final Comparator<MyNiftyClass>  BACKWARD =
>    #{ first, second ->  FORWARD.compare(second, first); };
>
>or a similar syntax (this part is not frozen yet).
>
>Rémi
>
>
>On 06/24/2011 10:03 AM, David Holmes wrote:
>> Derek,
>>
>> This has been raised a couple of time already. The reasoning is given in
>> the current Coin documentation (not sure if there's an official version
>> yet):
>>
>> http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html
>>
>> where it states:
>>
>> "Internally, a Java compiler operates over a richer set of types than
>> those that can be written down explicitly in a Java program. The
>> compiler-internal types which cannot be written in a Java program are
>> called non-denotable types. Non-denotable types can occur as the result
>> of the inference used by diamond. Therefore, using diamond with
>> anonymous inner classes is not supported since doing so in general would
>> require extensions to the class file signature attribute to represent
>> non-denotable types, a de facto JVM change. It is feasible that future
>> platform versions could allow use of diamond when creating an anonymous
>> inner class as long as the inferred type was denotable. "
>>
>> As you can see the door is open for relaxing the current rules in the
>> future.
>>
>> Cheers,
>> David Holmes
>>
>> Derek Foster said the following on 06/24/11 11:56:
>>> 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