Tuesday, August 17th 2010

Going to town with annotations

Annotations are a very useful feature of the Java language but sometimes an overdose of a good thing can be bad for you. Let me explain:

In our first example from Spring MVC we are using an annotation to map a method to a url. Without annotations this mapping information would likely have to go into an external file, perhaps an xml file, so annotations are very helpful in this case, because the code is much more straightforward when we have the two together. This is a good use of annotations:

@RequestMapping("/hello/kitty")
public String somePage(....) {
}

Going further and after a little googling I found this example of using annotations for validation, again in Spring:

  @NotBlank  
  @Length(max = 80)  
  private String name;  

  @NotBlank  
  @Email  
  @Length(max = 80)  
  private String email;  

  @NotBlank  
  @Length(max = 4000)  
  private String text;

Of course this can be a good thing, for example maybe you have a framework that uses the annotations to generate client-side Javascript validation code in addition to validating on the server side. But if that is not the case then we are starting to cross a line here, the line of using annotations to code things that could have been done perfectly well in plain Java code, and yet we are using annotations just because we can.

For comparison here is an imaginary piece of code of how this could look like in a universe where we are coding with pure servlets and without frameworks:

Validator v = new Validator(httpRequest);
v.item("name").notBlank().maxLength(80);
v.item("email").notBlank().maxLength(80).email();
v.item("text").notBlank().maxLength(4000);

if (v.error()) {
  // failure
} else {
  // success
}

This looks like about the same amount of code. So purely in terms of writing more high level code we haven't gained anything by using annotations in this case.

Googling some more I found this example:

public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
                        @Value("#{systemProperties['user.country']}"} String defaultLocale) {
    this.customerPreferenceDao = customerPreferenceDao;
    this.defaultLocale = defaultLocale;
}

Now we are passing actual code snippets as annotations. Perhaps there are legitimate uses of this but are we going a little overboard here? I mean what are we gaining for doing this instead of plainly writing:

this.defaultLocale = System.getProperty("user.country")

So what is the moral of this story? People get excited about new language features and try to do imaginative things with them. But when designing a library/api/framework it is a good idea to ask yourself first: "Could this be easily done in plain Java code?" before designing a complex meta-programing system to do the same thing with annotations.

What are we losing when we are using annotations instead of plain Java code? Besides making our program dependent on more complicated frameworks, annotations (being metadata and not code) are less powerful than actual code when used for programming scenarios. So to compensate, frameworks must define more annotations in order to cover all combinations and different use cases but also sometimes you are stuck when you need to do something the framework designer has not predicted.