java.util.Date
, but a specific BirthDateValue
instance. The Value Object hierarchy looks like that:abstract class ValueObject<T> {
private final T value;
public ValueObject(T pValue) {
value = pValue;
}
// shortened for brevity...
}
class ValueObjectDate extends ValueObject<Date> {
public ValueObjectDate(Date pDate) {
super((Date) pDate.clone()); // Date is mutable
}
// shortened for brevity...
}
class BirthDateValue extends ValueObjectDate {
public BirthDateValue(Date pValue) {
super(pValue);
}
}
ValueObject
, ValueObjectDate
and all other Value Object base classes are packaged in the platform used by the generator. BirthDateValue
and all other concrete subclasses are generated depending on the model. Such method signatures are more readable and passing a date of payment as a birth date by accident gets impossible. (Value Objects have other advantages, e.g. being read only, but that's not the point here.)All Value Objects use the
ValueObjectType
Hibernate User Type for persistence. Obviously the User Type has to know the type of the Value Object to create new instances and the type of the value inside it to map it onto the database column. But how to get the inner type? Our first approach was to configure it. As the Hibernate configuration is generated as well, that's no big deal. Nevertheless it's a bit lame and definitely not DRY.So the question is how to find the type parameter of a generic super class. In the given case the inner value is passed as constructor argument, so we can easily do better using reflection on the constructor parameter:
for (Constructor<?> c : pValue.getConstructors()) {Nice. That fixed our problem but made me think further. What if there would would be no method or field with the concrete type of the type parameter, e.g. when using collections as super classes and not defining any new methods? It's also possible to find the type using reflection:
if (c.getParameterTypes().length == 1) {
return c.getParameterTypes()[0];
}
}
// throw an exception
public Class<?> findType(Class<?> pValue) {A class knows if it's super class had type parameters using
if (pValue == Object.class) {
// throw an exception
}
// is it generic itself?
if (pValue.getTypeParameters().length > 0) {
return (Class<?>) pValue.getTypeParameters()[0].getBounds()[0];
}
// is the super class typed?
if (pValue.getSuperclass().getTypeParameters().length > 0) {
Type superClass = pValue.getGenericSuperclass();
ParameterizedType type = (ParameterizedType) superClass;
return (Class<?>) type.getActualTypeArguments()[0];
}
// go up to super class
return findType(pValue.getSuperclass());
}
getGenericSuperclass()
. If the class has a type parameter itself we don't know it due to erasure, but at least we get its lower bound. In all other cases we go up the class hierarchy. Finally a little test if it works as expected...@Test
public void testFindType() {
assertSame(Date.class,
userType.findType(ValueObjectDate.class));
assertSame(Date.class,
userType.findType(BirthDateValue.class));
}