In the StrongAspectJ paper, the authors consider this example as a case for separate “proceed signatures”:
Integer around(): //(1) call((Integer || Number) *()): Number proceed() { //(2) return new Integer(proceed().intValue()); //(3) }
In this example, the advice declares an “advice signature” (1) that returns Integer objects but a “proceed signature” (2) that returns any Number objects. This allows the advice to be applied to any method returning *any* type of Number while at the same time guaranteeing that only Integers are returned.
One could capture this semantics with a syntax such as:
jpi A Number()-> Integer();
Here, Number()
is the “expected interface”, i.e., the proceed
interface, while Integer()
is the provided interface.
Another example given in the StrongAspectJ paper is the following:
<N extends Number> N around(): call((Integer || Float) *(..)): N proceed() { N n = proceed(); while(n.intValue() > 100) n = proceed(); return n; }
Here the generic type parameter N
is used to refer to any concrete return type (here Integer
or Float
of Number
). Using the type parameter instead of just directly using Number
has the advantage that one knows that the advice *will* return an Integer
if a method call returning an Integer
is advised but also *will* return a Float
if a method call returning an Float
is advised.
In JPIs one could model this as:
jpi <N extends Number> A N()-> N();
Such a jpi could then be applied to calls such as…
Long l = System.currentTimeMillis();
… while the jpi jpi A Number()→ Number()
could only be applied in cases where the method returns a value of type Number.