Inter-type Declarations

Inter-type declarations are challenging to support using an annotation style. It's very important to preserve the exact same semantics between the code style and the annotation style. We also want to support compilation of a large set of AspectJ applications using a standard Java 5 compiler. For these reasons, in the initial release of AspectJ 5 we will only support inter-type declarations backed by interfaces when using the annotation style - which means it is not possible to introduce constructors or fields, as it would not be not possible to call those unless already weaved and available on a binary form.

Consider the following aspect:

     public aspect MoodIndicator {

        public interface Moody {};

        private Mood Moody.mood = Mood.HAPPY;

        public Mood Moody.getMood() {
          return mood;
        }

        declare parents : org.xyz..* implements Moody;

        before(Moody m) : execution(* *.*(..)) && this(m) {
           System.out.println("I'm feeling " + m.getMood());
        }
     }
         

This declares an interface Moody , and then makes two inter-type declarations on the interface - a field that is private to the aspect, and a method that returns the mood. Within the body of the inter-type declared method getMoody , the type of this is Moody (the target type of the inter-type declaration).

Using the annotation style this aspect can be written:

     @Aspect
     public class MoodIndicator {

        // this interface can be outside of the aspect
        public interface Moody {
          Mood getMood();
        };

        // this implementation can be outside of the aspect
        public class MoodyImpl implements Moody {
           private Mood mood = Mood.HAPPY;

           public Mood getMood() {
             return mood;
           }
        }

        // here is the actual ITD syntax when using @AspectJ
        // public static is mandatory
        // the field type must be the introduced interface. It can't be a class.
        @DeclareParents("org.xzy..*")
        public static Moody introduced = new MoodyImpl();

        @Before("execution(* *.*(..)) && this(m)")
        void feelingMoody(Moody m) {
           System.out.println("I'm feeling " + m.getMood());
        }
     }
         

This is very similar to the mixin mechanism supported by AspectWerkz. The effect of the @DeclareParents annotation is equivalent to a declare parents statement that all types matching the type pattern implement the interface whose @DeclareParents annotated aspect' field is type of (in this case Moody). Each method declaration of this interface are treated as inter-type declarations. Note how this scheme operates within the constraints of Java type checking and ensures that this has access to the exact same set of members as in the code style example.

Note that it is illegal to use the @DeclareParents annotation on an aspect' field whose type is not an interface. Indeed, the interface is the inter-type declaration contract that dictates which methods are introduced.

It is important to remember that the @DeclareParents annotated aspect' field that serves as a host for the inter-type declaration must be public static and initialized by some means. The weaved code will indeed delegate calls to this field when f.e. invoking:

     // this type will be affected by the inter-type declaration as the type pattern match
     package org.xyz;
     public class MoodTest {

        public void test() {
            // see here the cast to the introduced interface
            Mood mood = ((Moody)this).getMood();
            // will delegate to the aspect field "introduced" that host this inter-type declaration
            ...
        }
    }
         

It is perfectly possible to use an IoC framework to initialize the @DeclaredParents aspect' field. You must ensure though that the aspect field will be initialed prior the first inter-type declaration invocation it hosts.

If you need to only introduce a marker interface which defines no method - such as java.io.Serializable it is possible to use the following syntax.

Consider the following aspect:

     public aspect SerializableMarker {

        declare parents : org.xyz..* implements Serializable;
     }
         

Using the annotation style this aspect can be written:

     @Aspect
     public class SerializableMarker {

        @DeclareImplements("org.xyz..*")
        Serializable introducedNoMethods;
     }
         

The @DeclareImplements annotation on the aspect' field dictates the type pattern on which to introduce the marker interface.

In that case, as there is no method introduced, it is perfectly possible to have the aspect' field private, or not initialized. Remember that the field' type must be the introduced interface and cannot be class.