PROPOSAL: Abstract enums (version 2)

Derek Foster vapor1 at teleport.com
Wed May 6 21:16:27 PDT 2009


-----Original Message-----
>From: Bruce Chapman <brucechapman at paradise.net.nz>
>Sent: May 1, 2009 7:06 AM
>To: Derek Foster <vapor1 at teleport.com>, "coin-dev at openjdk.java.net" <coin-dev at openjdk.java.net>
>Subject: Re: PROPOSAL: Abstract enums (version 2)
>
>...
>Should this type parameter be explicit or implicit? - guess since enums 
>have an explicit extends, abstract ones could have an explicit type 
>parameter.
>
>your compilation section chooses implicit and says
>
>    abstract enum Foo {}
>
>shall be desugared to code resembling:
>
>    @AbstractEnum abstract class Foo<E extends Enum<E>> extends Enum<E> {}
>
>But I think it should be
>
>    abstract class Foo<E extends Foo<E>> extends Enum<E> {

I think you are right on this -- I had intended it to be that way, but mistyped. I will post a revised proposal.

>re @AbstractAnnotation
>
>I think you are confusing annotations in source files, with byte code 
>annotations which are different.

You are correct -- I was using a webpage for my reference which was not clear on which was being used in this case. I have since found the relevant sections in the class file specification which makes it more clear that the ACC_ENUM flag of the access_flags field in the class file is being used for this.

However, having done this, I'm not sure that the approach I suggested in my proposal is necessarily a bad idea -- the bits in the access_flags section of the class file format are a limited resource, after all, and over half of them (8 out of a possible 16) are already allocated. Still, by default it probably makes more sense to assume use of an ACC_ABSTRACTENUM flag rather than an @AbstractEnum annotation, with a comment that the @AbstractEnum approach could be used if necessary. I am in the process of revising the proposal, and will make this change when I do so.

> I don't think you'd need a new 
>annotation (of either sort) because the bytecode for an abstract enum 
>could have both the normal abstract annotation/modifier, and the 
>existing enum one, allowing the compiler to correctly determine that it 
>was in fact an abstract enum.

I don't think that this will work. The reason is that in the language as it exists now, in cases where enums have anonymous subclasses for individual enum constants, the declared enum type has both ACC_ENUM and ACC_ABSTRACT already. If the presence of these flags were the only distinguishing feature, these enum types would be impossible to distinguish from the user-specified enum supertypes that my proposal adds to the language.

It was for this reason that I chose to introduce the "@AbstractEnum" annotation in my proposal (which I will change to ACC_ABSTRACTENUM when I revise it) rather than trying to alter the meaning of the existing ACC_ENUM annotation. As Joe Darcy points out (in a message that I will answer next), altering the meaning of the existing annotation has the potential to cause unexpected consequences on the algorithms that manipulate it. The intent of my proposal was to sidestep this issue by adding a new annotation and leaving the old one to mean exactly what it already does, so that these algorithms would be unaffected.

Note that the existing algorithms that care about ACC_ENUM are basically concerned with the creation of and inheritance from the actual concrete enum types (and their anonymous subtypes). They are looking "down" the type hierarchy, which is not changed by my proposal, rather than "up" the type hierarchy, which my proposal affects. Therefore, these algorithms don't really need to care about whether or not a concrete enum type has only one abstract supertype (Enum<T>) or more than one (as my proposal would allow). By using an ACC_ABSTRACTENUM annotation instead of altering the meaning of ACC_ENUM, I can allow their behavior to continue unchanged.

>Could the abstract enum have type parameters, such that declared methods 
>were type polymorphic? (You enum for database table columns comes to 
>mind here) and if so, could the methods be abstract thereby forcing the 
>concrete enum to implement it?  The basic argument here is that if you 
>are fixing enums to be consistent with the rest of the language in terms 
>of inheritance, then maybe you should be in terms of type polymorphism 
>as well. If you did, it would put a different slant on the 
>implicit/explicit type parameter issue discussed above.

An interesting question. I mulled over this for a while, but was unable to come up with a solution I liked very much that was consistent with the rest of the language. For instance, if the type parameter is explicit, that leaves people typing things like:

abstract enum Foo<E extends Foo<E>> {    // Implicitly extends Enum<E>
    E getSuccessor() {return getDeclaringClass().getEnumConstants()[ordinal()+1];}
}

abstract enum Bar<E extends Bar<E>> extends Foo<E> {
}

enum Baz extends Bar<E extends Baz<E>> {
    SPORK, CHATOYANCY, WOMBAT, CHEESE;
}

which is quite annoyingly verbose but is what you would have to do if the user really had to specify the types correctly. This would require the compiler to do a bunch of extra checking as well, to make sure that all of the types were in fact correctly specified according to the necessary pattern. This seems like it would complicate basic use of the feature considerably (making the user type lots of boilerplate code), in order to handle a rare use case.

Another alternative would be to allow the user to just specify the name of the type parameter, and have its type be determined by the compiler (or omit even the name if it wasn't going to be used), such as:

abstract enum Foo<E> {     // Implicitly extends Enum<E extends Foo<E>>
    E getSuccessor() {return getDeclaringClass().getEnumConstants()[ordinal()+1];}
}

abstract enum Bar extends Foo { // No need for Bar<E> since not used in the body
}

enum Baz extends Bar {
    SPORK, CHATOYANCY, WOMBAT, CHEESE;
}

This is nicely compact and requires the user to only specify the one thing they can change (the name of the type parameter, not its type). However, it is inconsistent with how the language normally works (Foo<E> normally indicates that E extends Object, not that E extends Foo), which could be confusing.

I think on the whole, that referencing a subtype like this is a rare enough occurrence (I had to struggle to come up with plausible examples like the getSuccessor() method above) that we probably can do without language support for it. After all, in general, ordinary types can't access their subtypes like this at all, so the fact that it is even possible in the case of Enums is rather unusual.

If this feature is desperately needed, most of the obvious (to me, anyway) use cases can be handled with existing techniques like method return covariance anyway:

abstract enum Foo {   // Implicitly extends Enum<E extends Foo<E>>
    Foo getSuccessor() {return getDeclaringClass().getEnumConstants()[ordinal()+1];}
}

abstract enum Bar extends Foo {
}

enum Baz extends Bar {
    SPORK, FRIENDSHIP, WOMBAT, CHEESE;
    @Override
    Baz getSuccessor() { return (Baz)super.getSuccessor(); }
}

>Other than those issues, this proposal is looking good by me.

Thanks for the feedback!

Derek




More information about the coin-dev mailing list