February 24, 2005Annotation design patternsThroughout my work with EJBGen, EJB3 and TestNG, I have identified a couple of annotation-related patterns that have proven to be quite powerful. They are called "Annotation inheritance" and "Class-scoped annotations". Annotation Inheritance The idea is simply to extend the familiar inheritance mechanisms to annotations. Consider the following annotation:
And an example use:
If you try to look up annotations on the Test class using the standard reflection API, you will find none, since inheritance is not supported by JSR-175 (I submitted the idea but it was decided to keep the specification simple and leave this kind of behavior to tools, which is exactly what we are doing right now). A tool using this pattern would therefore see an @Author annotation on both BaseTest and Test. Since we follow the same overriding rules as Java, inheritance would also work on methods that have identical names and signatures. Where things get interesting is when you start considering "partial inheritance" (or partial overriding). Consider the following:
This time, the class Test is overriding the @Author annotation but only partially. Obviously, the date attribute in the @Author annotation will return "February 26th, 2005", but what is the value of name? Should it be null or "Beust"? My experience seems to indicate that while not necessarily the most intuitive, the latter form (partial overriding) is the one that is the most powerful. Partial overriding is a very effective way to implement "Programming by Defaults", which is a way of saying that you provide code with defaults that do the right thing for 80% of the cases. Basically, all you need to do to provide these defaults is to store these annotations on a base class and require client code to extend these base classes. Clients are then free to either override already-defined attributes or add their own, and the tool will gather all the attributes by collecting them throughout the inheritance hierarchy, starting from the subclass and working its way up to the base classes. In a next entry, I will describe the Class-Scoped Annotation pattern, and more importantly, how it can be combined with Annotation Inheritance to create some very elegant constructs. Posted by cedric at February 24, 2005 09:19 AM Comments
Just to clarify...in your first example, your Test class has no annotations. If I wanted all the defaults from the base class to be present, all I need to do is add @Author
Sorry, no, Noah. Try the following: import java.lang.annotation.*; @Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Date(name="Cedric", date="Today") @Date C:\t>javac -d . A.java Date.java && java -classpath . A A little simplistic Cedric but some interesting concepts. the first problem is that will generate a compiler error because the element lastName is not specified and it does not have a default clause in the definition of @Author, so you can't actually omit it. If you solve that problem by specifying defaults in the declaration of Author, then you might still be stuck because you might not be able to determine whether an element (like lastName in your last example) was ommitted, or was actually explicitly set to its default value. It all depends on the API you are using. With com.sun.mirror and com.sun.javadoc you can make the distinction, with java.lang.reflect you cannot. To solve that you could look up the default value, and if the annotation's value was the default you could ASSUME it was unspecified (and therefore should inherit the value declared in super), but that assumption might be wrong. You can make the assumption less likely to be wrong by making the default value something ridiculously meaningless, or something that is actually semantically invalid (to your application not to the compiler). All you have to do in the processing is to convert the declared default value, to the semantic default value when processing, (or cry foul if a value must be specified somewhere, that is if at the application level there is no default, even though at the implementation level there is). You could possibly write a general purpose method to do partial inheritance processing. Element declarations in an annotation type can themselves be annotated, so you could tell the processing algorithm what the application level default value was with an annotation. /** /** @interface Author { Applying " Inheritance" or "Partial Inheritance" mecanism to non Object Oriented concept >
Taking into account two facts: So at least it is surprising to see claims that "normal inherence" will be supported in any programming model which relays heavily on metaadat (including annotations). There is no such thing like "normal" inheritence of metadata. I just find it much safer and simpler not to try to apply concepts like inheritance to metadata as this can easily changed code into a mess. Michal Posted by: Michal at February 25, 2005 01:23 AMI'm not sure I agree with mac. Metadata may be a non object oriented concept, but in Java annotations are attached to object oriented constructs. They belong to the class (or feature) they are defined in. Fields and methods also belong to the class they are defined in and they can be inherited. Why shouldn't annotations be inherited according to the same rules that are already well understood by Java developers? That is one of the first things that occured to me when I was first learning about Java annotations, "so I can attach one of these to one of my Classes and read it back at runtime, cool... but it won't get inherited, hmmm.." And it doesn't seem that confusing to me as far as using annotations that can be inherited. The only confusing part that I can see would be writing tool or framework that has to correctly infer inheritance where none 'really' exists. I would have rather seen it as part of the official release because I'm not sure that widespread use of both inheritable and standard annotations would be a good thing. For instance what if a framework I use enables inheriting annotations that it processes. If I add my own annotations that the framwork knows nothing about then I have classes with both types in it. That sounds like trouble to me. But I don't buy the "it's not OO, leave it alone" argument: a big part of OO is rolling different language features into a Class and handling them in a consistent way. And as far as `Applying " Inheritance" or "Partial Inheritance" mecanism` : thats how everything else is done in Java so why not just phrase it `Applying the standard Java inheritance mecanism` Posted by: Mocky Habeeb at February 25, 2005 07:20 AMActually Annotations do support Inheritance of a kind. That kind though is that an annotation can be declared (using the meta-annotation @Inherited) to be inherited from classes to subclasses ONLY. Unfortunately (IMHO) many of the useful applications of inheritance would be inheriting from interfaces and JSR-175's inheritance mechanism does not allow this. Also the inheritance is a full inheritance, not the partial inheritance that Cedric discusses, and this, combined with the fact that we cannot extend an annotation type (therefore making our own similar annotation, being a kind of the other) and define different default values, makes annotations considerably less useful than they might otherwise be. Given these limitations in JSR-175 The concept of partial inheritance has proven useful to Cedric, and I can understand why. Since my comment yesterday I have written a meta-annotation for member elements, and a java.lang.reflect processing engine that can (for the examples above) return an Author object that exhibits the sort of partial inheritance Cedric discussed. This engine is general purpose and not restricted to one particular annotation type. Posted by: Bruce Chapman at February 25, 2005 03:45 PMAnnotations do support Inheritance through meta annotation (@Inherited). As can be seen from the listing below: import java.lang.annotation.*; @Inherited @Author(lastName = "Beust", date = "February 25th, 2005")
Annotation supports inheritance Post a comment
|