PROPOSAL: Abstract enums (version 2)
Bruce Chapman
brucechapman at paradise.net.nz
Thu May 7 03:28:47 PDT 2009
Derek Foster wrote:
> -----Original Message-----
>
>
>> 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.
>
>
Derek,
I was really thinking about an additional type parameter not the
implicit Enum one. Taking your database definition use case a step
further, (probably an overly complex example but ...) then with type
parameters you could do this
enum Access { PUBLIC, PRIVATE };
enum SqlType<T> {
// VARCHAR has type SqlType<String>
VARCHAR<String> {
public String getValue(ResultSet r, String name) {
return r.getString(name);
}
INTEGER<Integer> {
public Integer getValue(ResultSet r, String name) {
return r.getInt(name);
}
}
FLOAT<Float> {
public Float getValue(ResultSet r, String name) {
return r.getFloat(name);
}
}
public abstract T getValue(ResultSet r, String name);
}
/**
* This holds the shared definition of what it means
* to be a database table column identifier
* @param <T> The java type of the column.
*/
abstract enum TableColumnDef<T> {
// Note: No enum constants are declared here!
// This is just a supertype!
private SqlType<T> sqlType;
private String tableName;
private String columnName;
private Access access;
protected TableColumnDef(String tablename, String columnName, SqlType<T> sqlType, Access access) {
this.tableName = tableName;
this.columnName = columnName;
this.access = access;
this.sqlType = sqlType;
}
public String getTableName() {
return tableName;
}
public String getColumnName() {
return columnName;
}
public Access getAccess() {
return access;
}
@Override
final String toString() {
return tableName + "." + columnName;
}
public T getFrom(ResultSet r) {
// check ResultSet is for this table - ellided
return sqlType.getValue(r,columnName);
}
}
/** A specific table */
enum PersonTable<T> extends TableColumnDef<T> {
NAME_COLUMN has Type PersonTable<String>
NAME_COLUMN<String>("Name", SqlType.VARCHAR, Access.PUBLIC),
AGE_COLUMN<Integer>("Age", SqlType.INT, Access.PRIVATE),
WEIGHT_COLUMN<Float>("Weight", SqlType.FLOAT, ACCESS.PUBLIC);
private PersonTable(String columnName, SqlType<T> sqlType, Access access) {
super("PersonTable", columnName, sqlType, access);
}
}
/** Another specific table */
enum PetTable<T> extends TableColumnDef<T> {
PETNAME_COLUMN<String>("PetName", SqlType.VARCHAR, Access.PUBLIC),
SPECIES_COLUMN<String>("Species", SqlType.VARCHAR, Access.PUBLIC);
private PersonTable(String columnName, SqlType<T> sqlType, Access access) {
super("PetTable", columnName, sqlType, access);
}
}
And then you write
ResultSet r = ...
float weight = PersonTable.WEIGHT_COLUMN.getFrom(r);
Maybe its the strange stuff I write, but often I find that something I'd
like to encode as an enum, doesn't really work, because there is some
type variability represented by the various constants. Being able to
express this would be great (but probably isn't coin sized :( )
Bruce
More information about the coin-dev
mailing list