Aspects can declare members (fields, methods, and constructors) that are owned by other types. These are called inter-type members. Aspects can also declare that other types implement new interfaces or extend a new class. Here are examples of some such inter-type declarations:
This declares that each Server has a
boolean field named disabled,
initialized to false:
private boolean Server.disabled = false;
It is declared private, which means that it is
private to the aspect: only code in the aspect
can see the field. And even if Server has
another private field named disabled (declared in
Server or in another aspect) there won't be a name
collision, since no reference to disabled will be
ambiguous.
This declares that each Point has an
int method named getX with no
arguments that returns whatever this.x is:
public int Point.getX() { return this.x; }
Inside the body, this is the
Point object currently executing. Because the
method is publically declared any code can call it, but if there is
some other Point.getX() declared there will be a
compile-time conflict.
This publically declares a two-argument constructor for
Point:
public Point.new(int x, int y) { this.x = x; this.y = y; }
This publicly declares that each Point has an
int field named x, initialized
to zero:
public int Point.x = 0;
Because this is publically declared, it is an error if
Point already has a field named
x (defined by Point or by
another aspect).
This declares that the Point class implements the
Comparable interface:
declare parents: Point implements Comparable;
Of course, this will be an error unless Point
defines the methods required by Comparable.
This declares that the Point class extends the
GeometricObject class.
declare parents: Point extends GeometricObject;
An aspect can have several inter-type declarations. For example, the following declarations
public String Point.name;
public void Point.setName(String name) { this.name = name; }
publicly declare that Point has both a String field
name and a void method
setName(String) (which refers to the
name field declared by the aspect).
An inter-type member can only have one target type, but often you may wish to declare the same member on more than one type. This can be done by using an inter-type member in combination with a private interface:
aspect A {
private interface HasName {}
declare parents: (Point || Line || Square) implements HasName;
private String HasName.name;
public String HasName.getName() { return name; }
}
This declares a marker interface HasName, and also declares that any
type that is either Point,
Line, or Square implements that
interface. It also privately declares that all HasName
object have a String field called
name, and publically declares that all
HasName objects have a String
method getName() (which refers to the privately
declared name field).
As you can see from the above example, an aspect can declare that interfaces have fields and methods, even non-constant fields and methods with bodies.
AspectJ allows private and package-protected (default) inter-type declarations in addition to public inter-type declarations. Private means private in relation to the aspect, not necessarily the target type. So, if an aspect makes a private inter-type declaration of a field
private int Foo.x;
Then code in the aspect can refer to Foo's
x field, but nobody else can. Similarly, if an
aspect makes a package-protected introduction,
int Foo.x;
then everything in the aspect's package (which may or may not be
Foo's package) can access x.
The example below consists of one class and one aspect. The aspect
privately declares the assertion methods of
Point, assertX and
assertY. It also guards calls to
setX and setY with calls to
these assertion methods. The assertion methods are declared
privately because other parts of the program (including the code in
Point) have no business accessing the assert
methods. Only the code inside of the aspect can call those
methods.
class Point {
int x, y;
public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
public static void main(String[] args) {
Point p = new Point();
p.setX(3); p.setY(333);
}
}
aspect PointAssertions {
private boolean Point.assertX(int x) {
return (x <= 100 && x >= 0);
}
private boolean Point.assertY(int y) {
return (y <= 100 && y >= 0);
}
before(Point p, int x): target(p) && args(x) && call(void setX(int)) {
if (!p.assertX(x)) {
System.out.println("Illegal value for x"); return;
}
}
before(Point p, int y): target(p) && args(y) && call(void setY(int)) {
if (!p.assertY(y)) {
System.out.println("Illegal value for y"); return;
}
}
}