Thursday, May 24, 2007

Why java enum cannot extends?


UPDATE: There is an RFE (Request For Enhancement) in the Java Bug DB that you can vote for: RFE-6570766.
Please make this part of JDK 8!


The original need
I came around this issue in the end of 2004 when trying to parse Rinex files (GPS satellites data). Using JDK 5 I used enum to defined the file format. It was very clean, readable, and made the reading and writing of the parser quite easy. This system of using enum for static grammar declaration is actually used for the javac parser itself also.
The issue is that I needed to declared multiple enum for each type of lines and all enum entry contained the exact same data. So I ended up with something like:
public enum HeaderFormat {
info( "RINEX VERSION / TYPE","F9.2,11X,A1,19X" ,VERSION,TYPE),
creator( "PGM / RUN BY / DATE ","A20,A20,A20" ,PROGRAM,RUN_BY,DATE),
comment( "COMMENT ","A60" ,COMMENT),
endOfHeader("END OF HEADER ","60X" ,"");

private final RinexLineFormat _lineFormat;

HeaderFormat(String label, String format, String... fields) {
this._lineFormat = new RinexLineFormatImpl(label,format,fields);
}

public RinexLineFormat getLineFormat() {
return _lineFormat;
}
}
I needed to delegate all my enum static data to an external class, an I needed to delegate (copy/paste) this for all my XXXFormat enum.
I had one that look like:
public enum NavigationBodyFormat {
info(
"I2,1X,I2.2,1X,I2,1X,I2,1X,I2,1X,I2,F5.1,3D19.12",
SATELLITE_NUMBER,YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,SV_CLOCK_BIAS,SV_CLOCK_DRIFT,SV_CLOCK_DRIFT_RATE),
brodacastOrbit1(
"3X,4D19.12",
IODE,CRS,DELTA_N,M0),
brodacastOrbit2(
"3X,4D19.12",
CUC,ECCENTRICITY,CUS,SQRT_A),
brodacastOrbit3(
"3X,4D19.12",
TOE,CIC,OMEGA_BIG,CIS);

private final RinexLineFormat _lineFormat;

NavigationBodyFormat(RinexLineFormat lineFormat) {
_lineFormat = lineFormat;
}

NavigationBodyFormat(
String format, String... fields) {
this._lineFormat = new RinexLineFormatImpl(null,format,fields);
}

public RinexLineFormat getLineFormat() {
return _lineFormat;
}

public void read(String line, Map<String, Object> container) {
getLineFormat().read(
line,container);
}
}
I was not too happy about this but it was OK.

But, then we started to do a lot of JPA migrations and the need for property declaration and/or Method and Field references, and their usage in annotations was raised again.

When trying to solve this problem, I wrote jfrog reflect xref and found out that I needed to extends enum again.

Solution
I try to read all around this extends limitation Self types (aka type of this) from Peter Ahe, and for me it resumes to one point: ''You cannot extends a class that is self-referencing in its generics.'' I may be totally wrong here, but anyway I'll stick to that.

So, that's why java.lang.Enum cannot be extended. But if you allow only one extension that will replace the class Enum, it should be quite easy. So I will really like to see "abstract enum" appears in JDK 7. The Rinex parser code will then look like:
public abstract enum EnumFormat {
private final String _label;
private final String _format;
private final String[] _fields;
private final RinexLineReader _lineReader;

EnumFormat(String label, String format, String fields) {
this._label = label;
this._format = format;
StringTokenizer fieldsStringTokenizer = new StringTokenizer(fields, ",");
List fieldsList = new ArrayList();
while (fieldsStringTokenizer.hasMoreTokens()) {
fieldsList.add(fieldsStringTokenizer.nextToken());
}
this._fields = fieldsList.toArray(new String[fieldsList.size()]);
_lineReader = new RinexLineReaderImpl(this);
}

EnumFormat(
String label, String format, String... fields) {
this._label = label;
this._format = format;
this._fields = fields;
_lineReader = new RinexLineReaderImpl(this);
}

public String getLabel() {return _label;}
public String getFormat() {return _format;}
public String[] getFields() {return _fields;}
public RinexLineReader getLineReader() {return _lineReader;}

public void read(String line, Map<String, Object> container) {
getLineReader().read(
line,container);
}
}
And my format enum declaration very straight forward, without any java code just grammar declaration like:
public enum HeaderFormat extends EnumFormat {
info( "RINEX VERSION / TYPE","F9.2,11X,A1,19X" ,VERSION,TYPE),
creator( "PGM / RUN BY / DATE ","A20,A20,A20" ,PROGRAM,RUN_BY,DATE),
comment( "COMMENT ","A60" ,COMMENT),
endOfHeader("END OF HEADER ","60X" ,"");
}
and
public enum NavigationBodyFormat extends EnumFormat {
info(
"I2,1X,I2.2,1X,I2,1X,I2,1X,I2,1X,I2,F5.1,3D19.12",
SATELLITE_NUMBER,YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,SV_CLOCK_BIAS,SV_CLOCK_DRIFT,SV_CLOCK_DRIFT_RATE),
brodacastOrbit1(
"3X,4D19.12",
IODE,CRS,DELTA_N,M0),
brodacastOrbit2(
"3X,4D19.12",
CUC,ECCENTRICITY,CUS,SQRT_A),
brodacastOrbit3(
"3X,4D19.12",
TOE,CIC,OMEGA_BIG,CIS);
}

Using it for property
So, how "abstract enum" can solve some issues for Java 7 like:

First, I have to say that I really don't want to see "->" comes to Java. I used to be a C++ developer and removing from by brain the significance of "->" was a huge relief. So, please no more '''->'''

To target the above issues, I'm using Typesafe Reflections, which will provide me with 3 kind of "abstract enum": FieldDefinition, MethodDefinition, and PropertyDefinition.

The abstract enum java.lang.reflect.PropertyDefinition will look like:
public abstract enum PropertyDefinition {
private final Class modelClass;
private final String fieldName;
private final String getterMethodName;
private final String setterMethodName;
private Field reflectField;
private Method getterMethod;
private Method setterMethod;

PropertyDefinition() {
modelClass = getDeclaringClass().getEnclosingClass();
// some string mapping that can be driven by annotations for prefixes and so on
this.fieldName = name();
this.getterMethodName = "get"+StringUtils.capFirst(name());
this.setterMethodName = "set"+ StringUtils.capFirst(name());
}

public Field getField() {
...some field resolution
return reflectField;
}

public Method getSetterMethod() {
...some method resolution
return setterMethod;
}

public Method getGetterMethod() {
//...some method resolution
return getterMethod;
}

public Class getModelClass() {return modelClass;}
public String getFieldName() {return fieldName;}

// And then a nice list of helper and delegate methods like,
// They can aggregate annotations from fields and getters/setters methods
public Type getGenericType() {...}
public <t> T getAnnotation(Class<t> annotationClass) {...}
public Annotation[] getAnnotations() {...}
public boolean isAnnotationPresent(Class annotationClass) {...}
public Annotation[] getDeclaredAnnotations() {...}

// Here there is a need to type this
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException {...}
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException {...}
}

Then inside my classes I can explicitly, implicit by annotation, or fully implicitly if Java allows it, have:
public class MyModel {
private String firstName;
private String lastName;
private int age;

// No mandatory getters or setters, just the one I want
public String getFirstName() {return firstName;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}

public static enum properties extends PropertyDefinition {
firstName, lastName, age;
}
}

With this ability, java property and binding declaration are possible, readable, don't add any keywords or ugly "->" and becomes very clean and concise. The only draw back is the ''implicit'' or compile time verification between the enum entries name and the actual fields. I keep thinking about this issue and here are my views:
  • I don't want all my fields or methods to be accessible via these XXXDefinition enum,
  • I want to control the fields and methods that children class will expose for binding,
  • The compiler can always do a sanity check.

Using property in Annotations
So now, to solve more binding problems:
  • Associations in JPA. How to you link to the property on the other side in a type safe way?
  • Interceptor Binding. Passing a method reference in an annotation like @PostInsert().
  • And many more...

Since
"abstract enum" are suppose to still be enum they should be able to be used in annotations attributes like:
public @interface OneToMany {
FieldDefinition inverseField() default BaseObject.fields.id;
}

And so now linking properties will be type safe:
public class Person {
@OneToMany(inverseField = Dog.fields.owner)
private Set<Dog> dogs;
}

public class Dog {
@ManyToOne(inverseField = Person.fields.dogs)
private Owner owner;
}

I like it...

Annotation Injection Architecture

From AADA to AIA
Anyway Uri Boness has a point here, and AADA was answering it.
My first work on AADA worked around using APT, or pure AOP framework like AspectJ and AspectWerkz. My issue with these solutions were that the tools were way overkill for a simple thing like Annotation Injection. I just wanted something that is dynamically (or more exactly rule based) injecting annotations based on my business annotations.
So, what I needed is to activate some kind of rules when any framework (Hibernate, XFire, EJB3 container, Marshaller, ...) were calling the methods of java.lang.reflect.AnnotatedElement
It was the time of Java getting open source by Sun, so I thought about giving a shot on how it can be done, but dropped it by laziness...

Back on track
Now with Java 7 and the great features for framework like Hibernate:
  • erasing the erasure or reify generics
  • methods and function pointers
  • binding
I think it's time for a new adventure toward Annotation Injection.

Thursday, May 17, 2007

Type safe Reflections

After a discussion with other consultants, I finally found what is missing in Java when using annotations and frameworks. Especially the ones that need mapping like: O/R Mapping, Web Services, or UI.
Most people agree, it's cool to annotate your class so a framework (Hibernate, XFire, ...) will take care of them for you. It's clean, fast and highly maintainable.
But there is one itching point: ''How do you link 2 methods, fields by annotations?''
It's not an academic question: You need to do that very often.
In hibernate bidirectional associations there is of course 2 fields (one on each side), in UI framework you need to bind a model field to UI element, and so on.

Annotation parameters are from a well defined list of types (with array aggregation): Simple (String, int, boolean, ...), Class, Enumeration.
So there is no such things as fields and methods identifier that can be passed as parameters to annotations. In fact you can never identify a field or a method in a type safe way in Java.

So, here is my proposition:
Using "abtract enum" you can define a FieldDefinition abstract enum like in reflect project code browsable with xref.
This class will look like:
public abstract enum FieldDefinition {
private final Class modelClass;
private final String fieldName;
private Field reflectField;

FieldDefinition() {
modelClass = getClass().getDeclaringClass().getEnclosingClass();
this.fieldName = name();
}

public Field getField() {
if (reflectField == null) {
try {
reflectField = getModelClass().getDeclaredField(getFieldName());
}
catch (NoSuchFieldException e) {
throw new ObjectMappingException("Field " + this + " with name " + fieldName +
" does not exists in " + modelClass, e);
}
}
return reflectField;
}

public Class getModelClass() {return modelClass;}
public String getFieldName() {return fieldName;}

// And then a nice list of helper and delegate methods like
public Type getGenericType() {...}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {...}
public Annotation[] getAnnotations() {...}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {...}
public Annotation[] getDeclaredAnnotations() {...}
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException {...}
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException {...}
}

So now you can use it in your model class like in test classes jfrog reflect xref:
public class MyModel {
private String firstName;
private String lastName;
private int age;

public static enum fields extends FieldDefinition
{
firstName, lastName, age}
}

Now you can do: myPage.firstNameUIComponent.bind(MyModel.fields.firstName);
And the UI framework has full transparent access to your model field the way you want. This is ''Type safe Reflections''
Cool no...