
Annotated Setters
March 27, 2007Ever use reflection to derive the property name from a Method instance? It may end up looking something like this:
if (method.getName().startsWith("set")) {
// derive property name
String name = method.getName().substring(3);
StringBuilder sb = new StringBuilder(name);
sb.replace(0, 1, sb.substring(0, 1).toLowerCase());
String property = sb.toString();
// guard against false positives: e.g. setUp()
// use a Set to filter duplicates
Set fieldNames = new HashSet();
Field[] fields = instance.getClass().getDeclaredFields();
for (Field f : fields) {
fieldNames.add(f.getName());
}
fields = instance.getClass().getFields();
for (Field f : fields) {
fieldNames.add(f.getName());
}
if (fieldNames.contains(property)) {
// do stuff
}
}
With Annotations, the job can be much simpler. Here’s the Annotation class:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Setter {
public String forProperty();
}
Annotate a setter method and then it becomes a simple matter to see if the Method is a setter:
if (method.isAnnotationPresent(Setter.class)) {
String property = method.getAnnotation(Setter.class).forProperty();
// do stuff
}
The beans spec allows for setters without the word ’set’ in them. That’s why you should use Introspector, or not use beans at all.
+1 to Ricky. You should really do something like this with the Introspector class. If you want to find all the bean setters, do something like:
BeanInfo info = Introspector.getBeanInfo(beanClass, Object.class);
PropertyDescriptor[] beanProps = info.getPropertyDescriptors();
for(PropertyDescriptor prop : beanProps) {
String propName = prop.getName();
Method writeMethod = prop.getWriteMethod();
if(writeMethod != null) {
// Here you know the prop name and setter method
}
}
The reason this is better is that
1) you don’t have to write all the reflection and name stuff yourself and
2) the Introspector handles not only reflecting the bean, but also reflecting super-classes, etc
3) the Introspector will use an externally supplied BeanInfo if it exists, allowing bean authors to break from standard setter conventions
4) the Introspector caches the introspection info, which should improve performance if you’re doing this a lot.
I see how Introspector will find _all_ the setters, but what if I’m only interested in specific ones? @Setter then seems a better solution as I only need to annotate those mutators that I’m interested in.
Yes, you’d need an annotation just to pick out certain ones. Although I think in that case, I’d argue you should name your annotation more precisely for whatever purpose it’s being annotated.
You could also combine the two techniques – use Introspector to find all properties with setters, then check the resulting writeMethod’s annotations to further filter it down.