18 January 2008

Tags: annotations inheritance java

Following my post about the behaviour of annotations in an inheritance model, Mike Kaufman made a very interesting comment. The truth, as I said, is that annotations are technically inherited only if the annotation definition itself is annotated with @Inherited.

I must admit that while this is technically true, the facts are not so simple. Let me explain why I came to this test case. I had written a web service using the Metro (JAX-WS RI) project. With project Metro, web services are defined with annotations. For example, you would write :

@WebService(serviceName="myService")
public class MyService {
    @WebMethod
    public int sum(int x, int y) { return x+y; }
}

This is a very convenient (and easy) way of building web services. But services should always be defined through interfaces. Therefore, here’s I would like to do :

public interface IMyService {
   void sum(int x, int y);
}

..

@WebService(serviceName="myService")
public class MyService implements IMyService {
...
}

That’s a good starting point, but I like to say that the MyService class is nothing but a default implementation of my service. Moreover, annotating a class leads to readability problems when you mix several annotation frameworks. So I wanted to be able to annotate the interface.

@WebService
public interface IMyService {
  @WebMethod void sum(@WebParam(name="x") int x, @WebParam(name="y") int y);
}

Then implement like this :

@WebService(serviceName="myService")
public class MyService implements IMyService {
   public int sum(int x, int y) { return x+y; }
}

This has multiple advantages :

  • separation of interface and implementation (should always be done)

  • the interface may be shared for both server endpoint and client stubs

Note that I must copy down the @WebService annotation just to specify the service name (as multiple services may implement the same interface). There, you should already see a problem, but let’s continue : Metro does not allow this. You cannot annotate interfaces and hope subclasses will be ``annotated''. Bas point, but it’s rather logic :

package javax.jws;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE })
public @interface WebService
{
    String name() default "";

    String targetNamespace() default "";

    String serviceName() default "";

    String wsdlLocation() default "";

    String endpointInterface() default "";

    String portName() default "";
};

The @WebService annotation is not itself annotated with @Inherited. Then, some weeks later, I moved to Apache CXF. To my surprise, although CXF uses the very same JAX-WS annotations, those annotations can be set on interfaces rather than classes ! How could it be ? Are annotations inherited or not ? My previous post gave you a technical yet simple answer : in that particular case, annotations are not inherited.

That’s where Mike’s comment is interesting : it is a very complete explanation on why different frameworks did build their own annotations inheritance rules. To be clear, if an annotation has its retention policy set to runtime and is annotated with @Inherited, then the subclasses with have this annotation to their annotations list too. But this does not prevent a framework from building its own inheritance policy.

In CXF, when you declare a class as a webservice, CXF looks for superclasses and interfaces to get those annotations. This is a framework dedicated annotation inheritance rule.

With this second example, here you see the problem : although the technical truth is that annotations are not inherited by default, the facts are that frameworks define their own rules… Thanks Mike for giving me a great transition ;)