Tuesday, February 13, 2007

Annotations

This post is a few years too late. Annotations came out in Java 1.5, and we already are on Java 1.6. But I'd like to make a few comments on them, because I think they are a great example of a low-cost but a high-use feature.

Making improvements to a popular language is always difficult. You want each addition in functionality to be well worth it. Each addition should make things possible that were not possible before, and do so in a way that both fits with the spirit of the language, and results in the least possible disruption.

Java annotations are a good example of a good change. I find I use them more and more, and they enable things I now realize I in fact need. Did I need them before annotations were around? It didn't seem so, but now they are here I would find it difficult to live without them.

I should illustrate how useful they are with a made-up example. Let's say you want a simple webserver, one that took the path and used it to call into a dispatcher object. So you would call http://localhost/doSomething?foo=aaa&bar=bbb. This would use Java reflection to call a method Dispatcher.doSomething(String foo, String bar) . But at reflection time you don't know the names of arguments, since those are all compiled away. So, the first challenge is to figure out which argument is foo and which is bar. How can we do this? Before annotations, I would say you cannot do this. You could do something such as populate a String to String map, and pass that in instead of arguments, but this is awkward for the non-reflection callers, and also doesn't enforce important invariants at compile-time. Luckily for us, we do have annotations, and the annotations stick around at runtime (if you want them to). So, we can make an annotation defined as:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ParameterName {
String value();
}

And we can use it like so:

void doSomething(@ParameterName("foo") String foo,
@ParameterName("bar") String bar) {
...
}


Now reflection can match HTTP parameters up to the method arguments.

But the fun doesn't stop there. What if you wanted your delegator class to only have some methods accessible from the web? You could try to separate into another class that is only web methods, but that overly constraining on the design. Or, you could maintain a separate list of web-safe methods in a configuration file. That solution seems prone to mistakes - developers will forget to update the configuration file, then wonder why their new method isn't accessible. However, with annotations, you can simply tag methods with an annotation showing that they are web-safe. We can create a runtime method-level annotation called WebExecutable and use it like so:


@WebExecutable
void doSomething(@ParameterName("foo") String foo,
@ParameterName("bar") String bar) {
...
}


Now, your dispatcher can check for the annotation before it executes. Instant safety! You can imagine further annotations that may classify whether methods needs authentication or not, or other such improvements.

I've really been digging annotations. They make things possible that never were possible before, and don't introduce much complexity or confusion. There is some new syntax to learn, and some non-obvious things (such as "@interface"), but all in all, the part you have to look at the most is very easy and intuitive. I think it's an ideal addition to the language.

Next, I intend to look at generics, which has quite a different profile.

No comments: