[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [aspectj-users] execution join points and overridden methods
|
Hi Doug -
I agree that the semantics section is incomplete on this point.
How about something like this:
{When supplied with a method signature, the execution pointcut will
pick out join points whose signatures match [as described elsewhere...].}
If the pointcut signature specifies a declaring type, the pointcut will
only match methods declared in that type, or methods that override
methods declared in or inherited by that type.
{It will not match methods with
the same signature in a super class of that type. This enables the
pointcut to match overriding methods as it does implementations, for
consistency in behavior when subclassing. To instead pick out only
methods on a specific type, use the lexical PCD's "within" or
"withincode()".}
The text is {braces} is transitional or elaboration and subject to edits,
but I believe the middle paragraph is correct (both as a description of what
the semantics should be and what the implementation does). I'll put it in
the semantics guide unless there are objections.
Otherwise, replies inline...
> ------------Original Message------------
> From: Doug Orleans <dougo@xxxxxxxxx>
> To: aspectj-users@xxxxxxxxxxx
> Date: Tue, Aug-17-2004 4:06 PM
> Subject: Re: [aspectj-users] execution join points and overridden methods
>
> Wes Isberg writes:
> > The programming guide, semantics appendix is typically laconic:
> >
> > At a method execution join point, the signature [is] a method
> > signature whose qualifying type is the declaring type of
> > the method.
>
> I saw this sentence, but I decided it was irrelevant, or at least
> didn't tell the whole story: it specifies the signature of a
> method execution join point, but it doesn't specify how a method
> execution pointcut designator signature pattern matches it.
Good point. It only says what's being matched.
> > So in our example, the declaring type is A1.
>
> I think this is wrong: thisJoinPoint.getSignature().getDeclaringType()
> returns A2. Even if it were A1, it wouldn't explain why both
> pointcuts (with different qualifying types) contain the same join
> point.
Again, good point. The reflective access to the join point provides
the type of the join point. If, or since, the join point can be
picked out by many pointcuts, some of which specify the supertype,
the type specified in the pointcut might not be identical to the join
point type (though the join point type will be a subtype of the type
specified in the pointcut). [See also P.S. below.]
> > (I should have said "declared in A1" rather than "defined in A1";
> > though both are true, declare is what matters.) Same result if A1
> > were an interface.
>
> As far as I know, the whole declaration/definition distinction is just
> left over from C and doesn't exist in the Java language spec. A
> class declaration contains member declarations; a class type contains
> declared and inherited members.
Good point. You convinced me to abandon this analogy in favor of
"overriding" and "inheriting", which the JLS does use and which
together cover Liskov substitution for methods. The slightly tricky
part is realizing that it's not just overriding. E.g.,
declare warning: execution(* Super.*(..))
will emit a warning for any method declared in (any subtype of) Super,
if the method is declared in, or inherited by, Super. For example,
class SuperSuper { void m() {} }
class Super {} // inherits SuperSuper.m()
class Sub {
void m() {} // overrides SuperSuper.m() <-- DW
void p() {}
}
aspect A {
declare warning: execution(* Super.*(..)): "Hello, World!";
}
The implementation Sub.m() (at DW) is flagged by the warning.
We normally would not say that Sub.p() "overrides" any method
in Super; it overrides SuperSuper.m(). (Of course, p() is not
picked out because it is not declared or inherited in Super.)
So "overrides" is correct for subtypes, but the overridden
method may be inherited by the specified type.
Thanks for bringing this up and pushing it to a better conclusion.
Wes
P.S. - Is this wise? No one has objected to the rationale of Liskov
substitution; but is that what programmers expect or want here?
And what should happen when the programmer specifies a subtype of the
declaring type of the method? Now, we don't pick out the supertype(s)
implementation(s), which would seem to violate the rationale. It
seems to be the programmers' fault, but certainly could come about
via a refactoring that hoisted a method to a supertype. Personally,
I've come to rely on these semantics, but I wonder if others do.
P.P.S. - For completeness' sake, since in Java a method can be declared
in two unrelated interfaces but extended in one interface and/or
implemented in one class, it means that either of two unrelated types
can be used to specify that particular implementation of the method.
So for the types:
interface I { void m(); }
interface J { void m(); }
interface K extends I, J { }
class D implements K { public void m() {} } // match
The declaration D.m() would be flagged by any of
declare warning: execution(* I.*(..)): "Hello";
declare warning: execution(* J.*(..)): "Hello";
declare warning: execution(* K.*(..)): "Hello";
declare warning: execution(* D.*(..)): "Hello";
Again thisJoinPoint.getSignature().getDeclaringType() would return
D, but the type specified could be any of D, K, J, or I.