Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-users] Re: Simplification of generic / parameterized type matching in pointcut expressions

On 01/08/05, Shigeru Chiba <chiba@xxxxxxxxxxxxxxx> wrote:

> I have one comment on the documentation about this feature.  To be
> honest,
> It took long time for me to understand why List<String> is permitted
> without
> runtime type check.  That was because I was thinking about the following
> example:
>
> class List<T> {
>      T value;
>      List<T> next;
>      static T head(List<T> list) { return list.value; }
> }
>
> String foo() {
>      List<String> slist = ... ;
>      String s = List.head(slist);    // this call
>      return s;
> }
>
> I thought that call(* *(List<String>)) selects List.head(slist) since
> the type of slist is List<String>.  However, this is not allowed by
> AspectJ 5.
> Am I right?  Well, now I know call() uses a signature for pattern
> matching but
> the signature of generic types is not obvious unless you deeply
> understand the
> generics.

Call (and execution) match by (declared) signature as you say. The
simplification proposal for generics says that instead of trying to
write signature patterns with type variables, a generic member is
matched by the signature of its erasure. In your case the member has
signature:

  static T head(List<T>)

and the erasure of that signature is:

  static Object head(List)

so it won't be matched by call(* *(List<String>)) (and neither could
it have been in the original proposal either, because of the effects
of erasure).

The pointcut call(* *(List<String>)) *would* match a call to the
following method:

void foo(List<String> ls);

and the pointcut call(* *(List)) would match calls to both head and
foo. In general using the raw types in pointcut expressions remains
the easiest and simplest thing to do - the best use case for
parameterized types in pointcut expressions is with args() so that you
retain type checking in the advice body. Even here you need to be
aware of the limitations of runtime type checking with generics as
unchecked warnings may be issued by the compiler.

The pointcut args(List<String>) will match a call to, or execution of,
foo(List<String>). It also matches a call to, or execution of
head(List<T>), but generates an unchecked warning in this latter case.

To match with args only where it can be statically determined to be
type safe you have to write eg:

before(List<String> ls) : call(* *(List<String>)) && args(ls) {
  // work with ls....
}

In this case you will never see an unchecked warning.

We said it was a *simplified* proposal, but totally *simple* may never
be achievable with Java generics ;) We'd like to get as close as we
can...

> I might be only the guy who confuses this fact, but the final
> documentation
> might be clear if you take care of the explanation of this.

We will be sure to give the final documentation a good review to try
and make what is of necessity a complex subject as clear as possible.
Right now, we're still trying to do that in the implementation too!

-- Adrian
adrian.colyer@xxxxxxxxx


Back to the top