Static crosscutting

Advice declarations change the behavior of classes they crosscut, but do not change their static type structure. For crosscutting concerns that do operate over the static structure of type hierarchies, AspectJ provides forms of introduction.

Each introduction form is a member of the aspect defining it, but defines a new member of another type.

Member introduction

A method introduction looks like

Modifiers Type TypePattern . Id(Formals) { Body }
abstract Modifiers Type TypePattern . Id(Formals);

The effect of such an introduction is to make all the types in TypePattern support the new method. Interfaces in TypePattern will support the new method as well, even if the method is neither public nor abstract, so the following is legal AspectJ code:

interface Iface {}

aspect A {
    private void Iface.m() { 
        System.err.println("I'm a private method on an interface");
    }
    void worksOnI(Iface iface) {
        // calling a private method on an interface
        iface.m();
    }
}

A constructor introduction looks like

Modifiers TypePattern.new(Formals) { Body }

The effect of such an introduction is to make all the types in TypePattern support the new constructor. You cannot introduce a constructor onto an interface, so if TypePattern includes an interface type it is an error.

A field introduction looks like one of

Modifiers Type TypePattern.Id = Expression;
Modifiers Type TypePattern.Id;

The effect of such an introduction is to make all the types in TypePattern support the new field. Interfaces in TypePattern will support the new field as well, even if the field is neither public, nor static, nor final.

Any occurrence of the identifier this in the body of the constructor or method introduction, or in the initializer of a field introduction, refers to the target type from the TypePattern rather than to the aspect type.

Access modifiers

Members may be introduced with access modifiers public or private, or the default package-protected (protected introduction is not supported).

The access modifier applies in relation to the aspect, not in relation to the target type. So a member that is privately introduced is visible only from code that is defined within the aspect introducing it. One that is package-protectedly introduced is visible only from code that is defined within the introducing aspect's package.

Note that privately introducing a method (which AspectJ supports) is very different from introducing a private method (which AspectJ previously supported). AspectJ does not allow the introduction of the private method "void writeObject(ObjectOutputStream)" required to implement the interface java.io.Serializable.

Conflicts

Introduction may cause conflicts among introduced members and between introduced members and defined members.

Assuming otherPackage is not the package defining the aspect A, the code

aspect A {
    private Registry otherPackage.*.r;
    public void otherPackage.*.register(Registry r) {
        r.register(this);
        this.r = r;
    }
}

adds a field "r" to every type in otherPackage. This field is only accessible from the code inside of aspect A. The aspect also adds a "register" method to every type in otherPackage. This method is accessible everywhere.

If any type in otherPackage already defines a private or package-protected field "r", there is no conflict: The aspect cannot see such a field, and no code in otherPackage can see the introduced "r".

If any type in otherPackage defines a public field "r", there is a conflict: The expression

this.r = r

is an error, since it is ambiguous whether the introduced "r" or the public "r" should be used.

If any type in otherPackage defines any method "register(Registry)" there is a conflict, since it would be ambiguous to any code that could see such a defined method which "register(Registry)" method was applicable.

Conflicts are resolved as much as possible as per Java's conflict resolution rules:

A subclass can inherit multiple fields from its superclasses, all with the same name and type. However, it is an error to have an ambiguous reference to a field.
A subclass can only inherit multiple methods with the same name and argument types from its superclasses if only zero or one of them is concrete (i.e., all but one is abstract, or all are abstract).

Given a potential conflict between inter-type member declarations in different aspects, if one aspect dominates the other its declaration will take effect without any conflict notice from compiler. This is true both when the domination is declared explicitly in a "dominates" clause and when sub-aspects implicitly dominate their corresponding super-aspect.

Extension and Implementation

An aspect may introduce a superclass or superinterface onto a type, with the declarations

declare parents: TypePattern extends TypeList;
declare parents: TypePattern implements TypeList;

For example, if an aspect wished to make a particular class runnable, it might add an appropriate void run() method, but it should also change the type of the class to specify that it fulfills the Runnable interface. In order to implement the methods in the Runnable interface, the run() method must be publically introduced:

aspect A {
    declare parents: SomeClass implements Runnable;
    public void SomeClass.run() { ... }
}

Interfaces with members

Through the use of introduction, interfaces may now carry (non-public-static-final) fields and (non-public-abstract) methods that classes can inherit. Conflicts may occur from ambiguously inheriting members from a superclass and multiple superinterfaces.

Because interfaces may carry non-static initializers, the order of super-interface instantiation is observable. We fix this order with the following three properties: A supertype is initialized before a subtype, that initialized code runs only once, and initializers for supertypes run in left-to-right order. Consider the following hierarchy where {Object, C, D, E} are classes, {M, N, O, P, Q} are interfaces.

  Object  M   O
       \ / \ /
        C   N   Q
         \ /   /
          D   P
           \ /
            E

when a new E is instantiated, the initializers run in this order:

  Object M C O N D Q P E

Warnings and Errors

An aspect may specify that a particular join point should never be reached.

declare error: Pointcut: String;
declare warning: Pointcut: String;

If the compiler determines that a join point in Pointcut could possibly be reached, then it will signal either an error or warning, as declared, using the String for its message.

Softened exceptions

An aspect may specify that a particular kind of exception, if thrown at a join point, should bypass Java's usual static exception checking system and instead be thrown as a org.aspectj.lang.SoftException, which is subtype of RuntimeException and thus does not need to be declared.

declare soft: TypePattern: Pointcut;

For example, the aspect

aspect A {
    declare soft: Exception: execution(void main(String[] args));
}

Would, at the execution join point, catch any Exception and rethrow a org.aspectj.lang.SoftException containing original exception.

This is similar to what the following advice would do

aspect A {
    void around() execution(void main(String[] args)) {
        try { proceed(); }
        catch (Exception e) {
            throw new org.aspectj.lang.SoftException(e);
        }
    }
}

except, in addition to wrapping the exception, it also affects Java's static exception checking mechanism.

Statically determinable pointcuts

Pointcuts that appear inside of declare forms have certain restrictions. Like other pointcuts, these pick out join points, but they do so in a way that is statically determinable.

Consequently, such pointcuts may not include, directly or indirectly (through user-defined pointcut declarations) pointcuts that discriminate based on dynamic (runtime) context. Therefore, such pointcuts may not be defined in terms of

cflow
cflowbelow
this
target
args
if

all of which can discriminate on runtime information.