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...

No comments: