PROPOSAL: Abstract enums (version 2)

Derek Foster vapor1 at teleport.com
Thu May 7 00:17:00 PDT 2009


-----Original Message-----
>From: Joe Darcy <Joe.Darcy at Sun.COM>
>Sent: May 6, 2009 5:55 PM
>To: Derek Foster <vapor1 at teleport.com>
>Cc: Project Coin <coin-dev at openjdk.java.net>
>Subject: Re: PROPOSAL: Abstract enums (version 2)
>
>Hello.
>
>A few general comments and then some specifics.
>
>My general limited sympathy for this approach continues.  For example, 
>in the motivating example, the real enum types and enum constants could 
>be defined to return TableEnumGuts objects (perhaps with type 
>parameters) rather than delegating internally.  For correctness, the 
>TableEnumGuts objects should be immutable, etc.

Hi, Joe.

Thanks for the detailed reply.

It is true that an enum could be refactored in the manner you suggest above. However, please note that this approach has some notable drawbacks. For instance, it makes an implementation concern (sharing of implementation among related enum subclasses) into an interface concern (now everyone who uses the enum has to use "foo.getGuts().bar()" instead of "foo.bar()".

For instance, it means that all the clients of the class have to be modified. If there were two similar enums from which someone might want to do a conceptual "extract superclass" refactoring in order to reduce duplicate code, they cannot do it with this technique without modifying pretty much EVERY location where those enums are used (or at least, every location where a method call is made on them). If both the definition and all uses of the enums occur within the same codebase, which is modifiable, this can be done (although it is ugly, and annoying in that it may cause changes to a lot of code outside of the enums being so modified). However, if the enums are within a library which is delivered as a JAR file to various clients (say, released as open source on a website), then this refactoring would be a heavily breaking change to all of the clients that might use it. API definers are understandably fairly unwilling to make changes like this. In order to keep the interface stable, they would basically be forced into using some variation of the delegation approach I suggested, or some form of duplicate code.

>On the the specifics, as Bruce has pointed out, there is no 
>"@AbstractEnum" annotation or class file attributes to mark enum types.  
>There is an ACC_ENUM class modifier bit used for that purpose.  However, 
>today there are situations where the class file is marked with ACC_ENUM 
>and the class file is marked abstract
>...
>This proposal would complicate making sure those instance control checks 
>were still being enforced in all locations.

Although I understand and am sympathetic to the issue you are raising here, I think that your concern for its application in this case stems from an incorrect understanding of the details of my proposal.

I have already replied to Bruce on this, but to clear up some apparent confusion, I would like to point out that it was never part of my proposal to suggest that ACC_ENUM combined with ACC_ABSTRACT was an acceptable substitute for @AbstractEnum. Bruce suggested this in his recent email, but (as I just pointed out in my reply to him) I deliberately decided when writing my proposal not to take that approach, for basically exactly the reasons you list. Firstly, that it is ambiguous with respect to the existing uses of ACC_ENUM with ACC_ABSTRACT on enum classes with anonymous subtypes, and secondly, that it would require extensive re-analysis and possibly re-working of any algorithms that depended on the existing behavior and definition of ACC_ENUM. You have in the past stated how difficult it was to get these algorithms right, and I share your concern that changes to these would be difficult to perform correctly. Therefore, I tried to design my proposal so that changes are unnecessary.

For this reason, I left the behavior of ACC_ENUM unmodified, but introduced a new annotation, which I called @AbstractEnum in version 2 of the proposal but for which I will use a new class attribute (ACC_ABSTRACTENUM) in the upcoming version 3 which I will post tonight. ACC_ABSTRACTENUM is intended for exactly one purpose: allowing the compiler to detect illegal attempts to inherit abstract enums from normal classes and vice versa. This can't be done with ACC_ENUM because these classes will not have ACC_ENUM set on them (since that would change its current definition by widening it to include abstract supertypes of enums, which it is currently not used for). Note that ACC_ABSTRACTENUM will have no effect on serialization, Class.isEnum(), or any other existing code which currently makes decisions based on the presence or absence of ACC_ENUM.

So a typical hierarchy might be something like:

Enum<T>                           // ACC_ABSTRACT
abstract enum Foo { }             // ACC_ABSTRACT, ACC_ABSTRACTENUM
abstract enum Bar extends Foo { } // ACC_ABSTRACT, ACC_ABSTRACTENUM
enum Baz extends Bar { BAZ1; }    // ACC_ENUM
enum Qux extends Bar {            // ACC_ABSTRACT, ACC_ENUM
    QUX1 {
        void quux() {}
    };
    abstract void quux();
}

Thus, the existing algorithms that depend on ACC_ENUM meaning what it currently does should continue to work unmodified. ACC_ENUM will continue to be used as it currently is to detect illegal attempts to manipulate enums that are not declared abstract (illegal attempts such as cloning, constructing by reflection, illegally inheriting from, and so forth). Note that most of these operations are already illegal on any abstract class: you can't construct an abstract class by reflection, for instance, or try to clone one if neither its superclass Enum nor its subclass [a specific concrete enum] support cloning. Therefore, the only related issues for enums declared abstract are those which ACC_ABSTRACTENUM is intended to deal with, namely illegal inheritance.

>     536         if ((clazz.getModifiers() & Modifier.ENUM) != 0)
>     537             throw new IllegalArgumentException("Cannot 
>reflectively create enum objects");
>     538         if (constructorAccessor == null) 
>acquireConstructorAccessor();
>     539         return (T) constructorAccessor.newInstance(initargs);
>
>http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/6c7c0bccab55/src/share/classes/java/lang/reflect/Constructor.java 

For example, the above code would be unmodified under my proposal: Since an enum declared abstract would not have the Modifier.ENUM attribute, there would be no confusion or change in behavior: it would be treated by this code exactly like the existing Enum<T> class is. (Actually, there is another reason this particular code wouldn't change: this code could never be called for such a class anyway, since you can't call a constructor for an abstract class except from a subclass constructor, and doing so would thus perform the check on the non-abstract enum at the bottom of the inheritance hierarchy first. It would fail the check at that level before it ever got to trying to construct any abstract supertypes of that class.)

As per what I have said above, I believe that this proposal will not affect the validity of existing instance control checks. They are already designed to have effect on the "enum" classes and their possible anonymous subclasses, but not on the abstract supertypes of them (of which only one currently exists: Enum<T>). My proposal just adds the possibility of there being additional abstract supertypes in addition to Enum<T>, which will have the same access control flags as it does (with the exception of ACC_ABSTRACTENUM which these existing checks won't know about and will therefore ignore) and thus these new abstract supertypes will be treated in the same manner by these checks as Enum<T> is.

Derek

>On 04/30/09 11:20 PM, Derek Foster wrote:
>> Per feedback on the Project Coin mailing list, this proposal has been revised to provide more detail about how abstract enums affect serialization, class file format, the 'switch' statement, and 'Class.isEnum()', as well as additional text making more explicit the fact that this proposal is substantially different in both intent and implementation from various "extensible enums" proposals that have been considered and rejected in the past by the enum working group.
>>
>> AUTHOR: Derek Foster
>>
>> OVERVIEW
>>
>> The Java Enumerated type feature is unlike similar features in any other language which preceded it. In prior languages, an enumerated type was simply a collection of integer constants. In contrast, Java's enumerated type is a rich datatype that has more in common with classes than it has with a simple constants. Enumerated types can contain complex data, as well as methods to operate on that data. Individual enumerators may be subclasses of the enumeration class that contains them, and may override methods to provide enumerator-specific behavior.
>>
>> Unfortunately, there is one striking difference between Java's enumerations and classes that can become a significant inconvenience: While classes allow use of a superclass to hold data and methods which are shared between a variety of subclasses, Java enumerations have no such feature. In particular, every Java enumerated type must explicitly specify all of its data and methods that are not inherited from the base class Enum<T>. If there are a number of enumerations that share related behavior, each enumeration must specify it redundantly. The only way to share behavior between these enumerations is to manually implement use delegation.
>>
>> Note that the term "abstract enum" is somewhat imprecise, and has historically been interpreted (in particular, by the team responsible for defining how to add enumerated types to Java) to refer to a different concept than the one that is specified by this proposal. Specifically, the "extendable enums" approach, in which an abstract enumerated type which declares enumeration constants could be extended by another enumerated type which declares additional constants. Since there seems to have been some confusion on this point, I wish to specifically point out that the interpretation of the phrase "abstract enum" as it is used in this proposal is quite different from that assumed by the "extensible enums" feature that has been considered (and rejected) in the past. The extensible enums proposal was an attempt to define behavior of concrete subtypes of concrete enumerated types. This proposal, on the other hand, attempts to define the behavior of abstract uninstantiable supertypes of enumerated types. The focus of this proposal is not on adding "interface"-related features to enums -- it is on implementation sharing. As such, it is specifically designed to avoid the implementation and specification problems (difficulty with serialization, switch statements, etc.) that have given the phrase "abstract enums" a bad name in the past.
>>
>> For instance, imagine a system in which enumerations are used to access columns of tables in a relational database. There might be one enumeration for each table, and the enumerators of each type might represent the columns in that table. Presumably, each enumerator would contain the name of the table, as well as the name of a specific column, and perhaps other metadata, such as access privileges.
>>
>> It would seem natural to model such a system like this:
>>
>> enum Access { PUBLIC, PRIVATE };
>>
>> /** 
>>  * This holds the shared definition of what it means
>>  * to be a database table column identifier 
>>  */
>> abstract enum TableColumnDef {
>>     // Note: No enum constants are declared here!
>>     // This is just a supertype!
>>     private String tableName;
>>     private String columnName;
>>     private Access access;
>>     protected TableColumnDef(String tablename, String columnName, Access access) {
>>         this.tableName = tableName;
>>         this.columnName = columnName;
>>         this.access = access;
>>     }
>>     public String getTableName() {
>>         return tableName;
>>     }
>>     public String getColumnName() {
>>         return columnName;
>>     }
>>     public Access getAccess() {
>>         return access;
>>     }
>>     @Override
>>     final String toString() {
>>         return tableName + "." + columnName;
>>     }
>> }
>>
>> /** A specific table */
>> enum PersonTable extends TableColumnDef {
>>     NAME_COLUMN("Name", Access.PUBLIC),
>>     AGE_COLUMN("Age", Access.PRIVATE),
>>     WEIGHT_COLUMN("Weight", ACCESS.PUBLIC);
>>     private PersonTable(String columnName, Access access) {
>>          super("PersonTable", columnName, access);
>>     }
>> }
>>
>> /** Another specific table */
>> enum PetTable extends TableColumnDef {
>>     PETNAME_COLUMN("PetName", Access.PUBLIC),
>>     SPECIES_COLUMN("Species", Access.PUBLIC);
>>     private PersonTable(String columnName, Access access) {
>>          super("PetTable", columnName, access);
>>     }
>> }
>>
>> However, this cannot be done in Java, because inheritance from user-specified abstract types is not supported for enumerated types. (The only abstract type that is considered legal to inherit from is Enum<T>). The closest equivalent in Java as it currently exists is to model the desired system is something like this, using delegation instead of inheritance:
>>
>> class TableEnumGuts {
>>     private String tableName;
>>     private String columnName;
>>     private Access access;
>>     protected DatabaseEnum(String tablename, String columnName, Access access) {
>>         this.tableName = tableName;
>>         this.columnName = columnName;
>>         this.access = access;
>>     }
>>     public String getTableName() {
>>         return tableName;
>>     }
>>     public String getColumnName() {
>>         return columnName;
>>     }
>>     public Access getAccess() {
>>         return access;
>>     }
>>     @Override
>>     final String toString() {
>>         return tableName + "." + columnName;
>>     }
>> }
>>
>> enum PersonTable {
>>     NAME_COLUMN("Name"),
>>     AGE_COLUMN("Age"),
>>     WEIGHT_COLUMN("Weight");
>>     private TableEnumGuts guts;
>>     private PersonTable(String columnName, Access access) {
>>         guts = new TableEnumGuts("PersonTable", columnName, access);
>>     }
>>     public String getTableName() {
>>         return guts.getTableName();
>>     }
>>     public String getColumnName() {
>>         return guts.getColumnName();
>>     }
>>     public Access getAccess() {
>>         return guts.getAccess();
>>     }
>>     @Override
>>     final String toString() {
>>         return guts.toString();
>>     }
>> }
>>
>> enum PetTable extends TableEnum {
>>     PETNAME_COLUMN("PetName"),
>>     SPECIES_COLUMN("Species");
>>     private TableEnumGuts guts;
>>     private PersonTable(String columnName) {
>>         guts = new TableEnumGuts("PersonTable", columnName, access);
>>     }
>>     public String getTableName() {
>>         return guts.getTableName();
>>     }
>>     public String getColumnName() {
>>         return guts.getColumnName();
>>     }
>>     public Access getAccess() {
>>         return guts.getAccess();
>>     }
>>     @Override
>>     final String toString() {
>>         return guts.toString();
>>     }
>> }
>>
>> Note how much more code (and worst of all, boilerplate duplicate code) is required in the second example, since each enumerated type must provide forwarding methods for each method in the 'TableEnumGuts' class.
>>
>> In one example that the author of this proposal was forced to deal with, there were 30 such enumerated types which conceptually extended the same base class, each of which had to implement that conceptual inheritance by delegating four or so methods to an internal "guts" object. This meant that any time time the signature of any of those four methods had to be changed, the change had to take place in 30 different forwarding methods as well. In another case (working for a different company) there were 10 such methods, duplicated over four conceptual subclasses, with similar problems.
>>
>> This proposal attempts to solve the above problem by defining the semantics of abstract enumerated types, and what it means for an enumerated type to have an abstract supertype, so as to allow code similar to the first example to work as expected in a Java compiler.
>>
>>
>> FEATURE SUMMARY:
>>
>> Defines the meaning of inheritance from abstract supertypes for enumerated types.
>>
>> Note that this proposal specifically does not allow abstract enumerated types to have enumerators, since the semantics of these create difficult problems with inheritance (and open, in the words of one evaluator to a Sun bug submission, "a can of worms", related to the defunct "extendable enums" idea that has been proposed and rejected in the past.).
>>
>>
>> MAJOR ADVANTAGE:
>>
>> Eliminates a missing feature between java enumerations and java classes, and restores a feature of the "typesafe enumeration" pattern that is not supported by Java enumerations as they are currently implemented.
>>
>>
>> MAJOR BENEFIT:
>>
>> Elimination of duplicate code when related enumerated types need to be declared. Allows subclassing within enumeration types. Eases maintenance of families of enumerated types. Makes implementation sharing among related enumerated types work in a familiar manner analogous to how it works with classes.
>>
>>
>> MAJOR DISADVANTAGE:
>>
>> There will be some work in the compiler necessary to implement this feature.
>>
>> Some people would like to be able to have concrete enums extend each other ("extensible enums"). This proposal does not attempt to provide such a feature, since it has been determined to be very difficult to specify and implement in the past.
>>
>>
>> ALTERNATIVES:
>>
>> Note the delegation workaround being used above. This allows subtypes to share code, but results in potentially large numbers of forwarding methods and difficulties with code maintenance.
>>
>>
>> EXAMPLES
>>
>> SIMPLE EXAMPLE:
>>
>> abstract enum SuperEnum {
>>     // Note: no enum constant declarations allowed here!
>>     int getTheAnswerToTheUltimateQuestion() {
>>          return 42;
>>     }
>> }
>>
>> enum MyEnum extends SuperEnum {
>>      MY_ENUMERATOR;
>> }
>>
>> class DeepThought {
>>     Object returnTheAnswer() {
>>         MyEnum[] values1 = MyEnum.values(); // OK
>>         MyEnum values2 = MyEnum.valueOf("MY_ENUMERATOR"); // OK
>>         SuperEnum values1 = SuperEnum.values(); // Error: No such method
>>         SuperEnum values2 = SuperEnum.valueOf("whatever"); // Error: No such method
>>         return MY_ENUMERATOR.getTheAnswerToTheUltimateQuestion();
>>     }
>> }
>>
>> ADVANCED EXAMPLE:
>>
>> abstract enum Base1 {
>>     protected final int sharedData;
>>     public int getSharedData() {return sharedData;}
>>     protected Base1(int sharedData) {
>>         this.sharedData = sharedData;
>>     }
>> }
>>
>> abstract enum Base2 extends Base1 {
>>     protected final int otherSharedData;
>>     public int getOtherSharedData() {return sharedData;}
>>     protected Base2(int sharedData, int otherSharedData) {
>>         super(sharedData);
>>         this.otherSharedData = sharedData;
>>     }
>> }
>>
>> enum Instances extends Base2 {
>>     INSTANCE1(10, 100),
>>     INSTANCE2(20, 200),
>>     INSTANCE3(30, 300) {
>>          public int getSharedData() { return 40; }
>>     }
>>     private Instances(int sharedData, int otherSharedData) {
>>         super(sharedData, otherSharedData);
>>     }
>> }
>>
>> class AClient {
>>     void someFunc() {
>>         assert INSTANCE1.getSharedData()==10;
>>         assert INSTANCE2.getSharedData()==20;
>>         assert INSTANCE3.getSharedData()==40;
>>         Instances[] values = instances.values(); // OK
>>         Base2[] values = Base2.values(); // Error: No such method.
>>     }
>> }
>>
>> DETAILS
>>
>> SPECIFICATION:
>>
>> A concept of an "abstract enum" shall be added to the Java language, denoted by prepending the word "abstract" to an enumerated type declaration, with restrictions as described below.
>>
>> Rule 1: An enumerated type shall either declare no supertype, or shall declare a supertype which is an abstract enumerated type.
>>
>> Rule 2: It shall be a compile-time error for an ordinary class to extend an enumerated type, either abstract or not.
>>
>> Rule 3: Both abstract and non-abstract enumerated types shall implicitly have a supertype (not necessarily a direct supertype) of Enum<E extends Enum<E>>. Thus, within the body of an abstract enumerated type, access to methods of this supertype is allowed.
>>
>> Rule 4: Abstract enumerated types shall not declare enumerators. The body of an abstract enumerated type shall be syntactically and semantically the same as that of an ordinary abstract class which extends its supertype (see rule 3), except with the restriction that all constructors shall be declared with 'protected' or 'package' access (since the class is abstract and cannot declare instances, constructors can only be invoked by subclasses so there is no need to make them more public than necessary).
>>
>> Rule 5: In an abstract enum, the compiler shall not generate the methods that it would generate for a non-abstract enumerated type (specifically, the "values()" or "valueOf(String) methods), for an abstract enumerated type. (Since it is illegal for there to be enumerators declared in an abstract enum, these methods would serve no purpose.)
>>
>> Rule 6: It shall be illegal for the expression used in a 'switch' statement to be an abstract enum type.
>>
>>
>> Specifically, section 8.9 of the JLS3, which currently reads:
>>
>> "
>> An enum declaration has the form:
>>
>>     EnumDeclaration:
>>     	ClassModifiers_opt enum Identifier Interfaces_opt EnumBody
>> "
>>
>> shall be modified to read:
>>
>> "
>> An enum declaration has the form:
>>
>>     EnumDeclaration:
>>     	ClassModifiersopt enum Identifier Super_opt Interfaces_opt EnumBody
>> "
>>
>> Also, section 8.1.1.1 of the JLS3, which currently reads:
>>
>> "Enum types (§8.9) must not be declared abstract; doing so will result in a compile-time error. It is a compile-time error for an enum type E to have an abstract method m as a member unless E has one or more enum constants, and all of E's enum constants have class bodies that provide concrete implementations of m. It is a compile-time error for the class body of an enum constant to declare an abstract method."
>>
>> shall be modified to read:
>>
>> "Enum types (§8.9) may be declared abstract. If an enum type is declared abstract, it may declare abstract methods. However, it is a compile-time error for an abstract enum type to declare enum constants. Furthermore, it is a compile-time error for any constructor declared by an abstract enum type to have 'public' or 'private' access.
>>
>> It is a compile-time error for a non-abstract enum type E to have an abstract method m as a member (either explicitly declared or implicitly inherited from an abstract enum supertype) unless E has one or more enum constants, and all of E's enum constants have class bodies that provide concrete implementations of m. It is a compile-time error for the class body of an enum constant to declare an abstract method.
>> "
>>
>> also, in the same section, the text which currently reads:
>>
>> "If the enum type has no constructor declarations, a parameterless default constructor is provided (which matches the implicit empty argument list). This default constructor is private."
>>
>> shall be modified to read:
>>
>> "If the enum type has no constructor declarations, a parameterless default constructor is provided (which matches the implicit empty argument list). This default constructor is protected if the enum is abstract, and private if it is non-abstract."
>>
>> and in the same section, the text which currently reads:
>>
>> "The direct superclass of an enum type named E is Enum<E>."
>>
>> shall be modified to read:
>>
>> "A non-abstract enum type named E has a superclass (either direct or indirect via one or more abstract enums) of Enum<E>."
>>
>> and in the same section, the text which currently reads:
>>
>> "
>> It is a compile-time error for an enum to declare a finalizer. An instance of an enum may never be finalized.
>>
>> In addition, if E is the name of an enum type, then that type has the following implicitly declared static methods:
>> "
>>
>> shall be modified to read:
>>
>> "
>> It is a compile-time error for an enum (whether abstract or not) to declare a finalizer. An instance of an enum may never be finalized.
>>
>> In addition, if E is the name of a non-abstract enum type, then that type has the following implicitly declared static methods:
>> "
>>
>> and in the same section, the text which currently reads:
>>
>> "
>> It is a compile-time error to reference a static field of an enum type that is not a compile-time constant (§15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to itself or to an enum constant of the same type that is declared to the right of e.
>> "
>>
>> shall be modified to read:
>>
>> "
>> It is a compile-time error for a non-abstract enum type to reference one of its static fields which is not a compile-time constant (§15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to itself or to an enum constant of the same type that is declared to the right of e. Note however that any code within a non-abstract enum type may legally reference static fields of its abstract enum supertypes, if such supertypes and fields exist.
>> "
>>
>>
>>
>>
>> and in section 14.11 ('The switch statement'), the text which reads:
>>
>> "The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, or an enum type (§8.9), or a compile-time error occurs."
>>
>> shall be modified to read:
>>
>> "The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, or a non-abstract enum type (§8.9), or a compile-time error occurs."
>>
>>
>>
>> COMPILATION:
>>
>> An abstract enumerated type with no declared supertype, such as:
>>
>> abstract enum Foo {
>>    ,,,
>> }
>>
>> shall be desugared to code resembling:
>>
>> @AbstractEnum
>> abstract class Foo<E extends Enum<E>> extends Enum<E> {
>>     ...
>> }
>>
>> where "@AbstractEnum" is a private compiler-generated annotation similar to the one which is currently used to mark normal enumerated types, and in the same package. Like the annotation which is currently used to mark enumerated types, the @AbstractEnum annotation shall not be visible to ordinary users in source code form -- it exists solely in class files, to allow the compiler to distinguish an abstract class which was declared with the "abstract enum" syntax from one which was declared with the "abstract class" syntax, for the purpose of determining whether an attempt by one class to inherit from another (which may not be supplied in source code form) is legitimate.
>>
>> An abstract or non-abstract enumerated type with a declared supertype (which must be an abstract enumerated type), such as:
>>
>> abstract enum Foo1 extends Bar {
>>    ...
>> }
>>
>> enum Foo2 extends Bar {
>>    ...
>> }
>>
>>
>> shall be desugared to code resembling:
>>
>> @AbstractEnum
>> abstract class Foo1<E extends Enum<E>> extends Bar<E> {
>>    ...
>> }
>>
>> @...
>> class Foo2 extends Bar<Foo2> {
>>    ...
>> }
>>
>>
>> When compiling such a class, the compiler shall emit a compile-time error if an enum (abstract or not) extends a supertype S unless the following is true:
>>
>> 1) Either S is declared as an "abstract enum" (if specified in source code form) or S is declared with the @AbstractEnum annotation (if specified in binary class file form), or the erasure of S is the type java.lang.Enum.
>>
>> 2) Regardless of whether S and its supertypes are declared in source code form or in binary class file form, all supertypes of S shall obey these same constraints.
>>
>> Furthermore, if a normal class is declared like so:
>>
>> class Bar extends Baz {
>> }
>>
>> it shall be a compile-time error if class Baz is declared in source code to be an abstract enum, or if its binary class file representation has the @AbstractEnum annotation.
>>
>> The Java compiler currently generates certain methods, such as values() and valueOf(String) for enumerated types. It shall not generate these for abstract enumerated types. That is, the following shall be a compile-time error:
>>
>> abstract enum Foo {
>> }
>>
>> void bar() {
>>     Foo[] foo1 = Foo.values(); // Error: no such method as 'Foo.values()'
>>     Foo foo2 = Foo.valueOf("whatever"); // Error: no such method as 'Foo.valueOf(String)'
>> }
>>
>>
>> SIMPLE EXAMPLE:
>>
>> These classes might be desugared to:
>>
>> @AbstractEnum
>> abstract class SuperEnum<E extends Enum<E>> extends Enum<E> {
>>     int getTheAnswerToTheUltimateQuestion() {
>>          return 42;
>>     }
>> }
>>
>> @...
>> class MyEnum extends SuperEnum<MyEnum> {
>> // The majority of this non-abstract enumerated type,
>> // including generated methods and annotations, is exactly
>> // the same as the Java compiler would generate
>> // prior to this proposal. However, note that due to
>> // this proposal, it has a supertype of "SuperEnum<MyEnum>"
>> // instead of "Enum<MyEnum>".
>> }
>>
>> ADVANCED EXAMPLE:
>>
>> These classes might be desugared to:
>>
>> @AbstractEnum
>> abstract class Base1<E extends Enum<E>> extends Enum<E> {
>>     protected final int sharedData;
>>     public int getSharedData() {return sharedData;}
>>     protected Base1(int sharedData) {
>>         this.sharedData = sharedData;
>>     }
>> }
>>
>> @AbstractEnum
>> abstract class Base2<E extends Enum<E>> extends Base1<E> {
>>     protected final int otherSharedData;
>>     public int getOtherSharedData() {return sharedData;}
>>     protected Base2(int sharedData, int otherSharedData) {
>>         super(sharedData);
>>         this.otherSharedData = sharedData;
>>     }
>> }
>>
>> @...
>> class Instances extends Base2<Instances> {
>> // The majority of this non-abstract enumerated type,
>> // including generated methods and annotations, is exactly
>> // the same as the Java compiler would generate
>> // prior to this proposal. However, note that due to
>> // this proposal, it has a supertype of "Base2<Instances>"
>> // instead of "Enum<Instances>".
>> }
>>
>>
>> TESTING:
>>
>> Testing can be accomplished by declaring various abstract enumerated types, having them extend each other, and having non-abstract enumerated types extend them. Normal inheritance relations (method overriding, etc.) should exist among the resulting types.
>>
>> Furthermore, at least one test should ensure that an attempt to extend an abstract enum (either specified in source or binary form) with a normal class is considered illegal, as is an attempt to have a non-abstract enum or an abstract enum extend a class which is not an abstract enum.
>>
>> Furthermore, a test should ensure that an attempt to use an expression whose compile-time type is that of an abstract enum in a 'switch' statement is considered illegal.
>>
>> Furthermore, a test should ensure that an attempt to reference the "values()" or "valueOf()" methods of an abstract enum is considered illegal.
>>
>>
>> LIBRARY SUPPORT:
>>
>> No changes to supporting libraries are needed, except for the addition of the "@AbstractEnum" annotation type as mentioned above.
>>
>> REFLECTIVE APIS:
>>
>> The "Class.isEnum()" method shall return false when invoked on an abstract enum. (This is necessary to avoid unexpected side effects in methods which currently depend on identifying concrete enumerated types with this method. Technically, despite the syntax used to declare them, abstract enums aren't really enums -- they are just supertypes of them, and so should be treated basically exactly the same as class Enum<T> is for the purposes of reflection, etc. This should be documented clearly in the javadoc for Class.isEnum())
>>
>> It may be desirable to create a "Class.isAbstractEnum()" convenience method to detect the @AbstractEnum annotation. For consistency, such a method should probably return 'true' when invoked on the Enum<T> supertype as well.
>>
>>
>> OTHER CHANGES:
>>
>> Javadoc might possibly need to be updated to handle the possibility of generating documentation for an abstract enumerated type.
>>
>> In order to allow abstract supertypes to be added or removed from an abstract enum without making it impossible to read back existing serialized data, it will be necessary to modify the Java serialization specification as follows:
>>
>> In section "1.12 Serialization of Enum Constants", the following text shall be added at the end of the section:
>>
>> "
>> Furthermore, in order to preserve the ability to deserialize enumerated constants which may have been serialized with an enumerated type having a different type hierarchy from what was written (due to the addition or deletion of "abstract enum" supertypes), the type of every enumerated constant shall be serialized and deserialized as if the immediate supertype of the declared non-abstract enum type which defines the constant was java.lang.Enum. In particular, a type such as:
>>
>> abstract enum Foo {
>> }
>>
>> abstract enum Bar extends Foo {
>> }
>>
>> enum Baz extends Bar {
>>     QUX;
>> }
>>
>> shall exist in serialized form exactly as if it were declared as:
>>
>> enum Baz { // implicit supertype of Enum<T extends Enum<T>>
>>     QUX;
>> }
>> "
>>
>> MIGRATION:
>>
>> New abstract enumerated types can be created, as desired, to do code sharing between existing non-abstract enumerated types in an existing code base.
>>
>> The above "OTHER CHANGES" section ensures that adding or removing abstract supertypes of an enum will not affect the ability to serialize or deserialize instances of it.
>>
>> COMPATIBILITY
>>
>> BREAKING CHANGES:
>>
>> Since this syntax was previously considered a syntax error, no previously valid programs will be invalidated by it.
>>
>> EXISTING PROGRAMS:
>>
>> This feature should not change the semantics of existing class files. The features being added should desugar to normal method calls, class declarations, etc. Hence, I do not anticipate problems interacting with existing class files.
>>
>> REFERENCES
>>
>> EXISTING BUGS:
>>
>> Add language support for abstract Enum's
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6507006
>>
>> allow "abstract enum AbstractEnum" and "enum MyEnum extends AbstractEnum"
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222244
>>
>> Support for public abstract enum declaration
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766
>>
>> (The original enum proposal, the comments for which allude to the possibility of abstract enums)
>> Bug ID: 4401321 Add type-safe enums to Java
>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4401321
>>
>>
>> URL FOR PROTOTYPE (optional):
>>
>> Although not implemented in response to this specific proposal, Fred Simon has implemented a substantially similar feature in Kijaro:
>>
>> http://tech.puredanger.com/2008/01/10/property-enum/




More information about the coin-dev mailing list