From: acolyer Date: Fri, 17 Jun 2005 13:53:32 +0000 (+0000) Subject: extensive updates to the generics chapter to describe pointcut matching with generic... X-Git-Tag: PRE_ANDY~138 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2547987d0678849041168566c0e0e0c547cbe997;p=aspectj.git extensive updates to the generics chapter to describe pointcut matching with generic and parameterized types. Not quite finished yet, but this is a long way forward... - AMC. --- diff --git a/docs/adk15ProgGuideDB/generics.xml b/docs/adk15ProgGuideDB/generics.xml index 5e0f5b51d..ff74040e1 100644 --- a/docs/adk15ProgGuideDB/generics.xml +++ b/docs/adk15ProgGuideDB/generics.xml @@ -298,13 +298,27 @@ runtime ask if an object is an instanceof a parameterized type. + + Generics in AspectJ 5 + + AspectJ 5 provides full support for all of the Java 5 language features, including generics. Any legal Java 5 program is a + legal AspectJ 5 progam. In addition, AspectJ 5 provides support for generic and parameterized types in pointcuts, inter-type + declarations, and declare statements. Parameterized types may freely be used within aspect members, and support is + also provided for generic abstract aspects. + + Matching generic and parameterized types in type patterns + + The foundation of AspectJ's support for generic and parameterized types in aspect declarations is the extension of type + pattern matching to allow matching against generic and parameterized types. + + The type pattern "Foo" matches all types named Foo, whether they be simple types, generic types, or parameterized types. So for example, Foo, @@ -313,7 +327,8 @@ AspectJ 5 also extends the specification of type patterns to allow explicit matching of generic and parameterized - types. + types by including one or more type parameter patterns inside angle braces (< >) immediately + after the type pattern. For example, List<String> ' - - TypeParameter ::= Identifier TypeBound? - - TypeBound ::= 'extends' ReferenceType AdditionBoundList? | - 'super' ReferenceType AdditionalBoundList? - - AdditionalBoundList ::= AdditionalBound AdditionalBoundList | - AdditionalBound - - AdditionalBound ::= '&' ReferenceType - ]]> - A simple identifier occuring in a type parameter list will be treated as a type name unless it has previously - been declared as a type variable in a TypeParameterList. Some simple examples of - type patterns follow: + A simple identifier (such as String) occuring in a type parameter list will be treated as a type name unless + a type variable of that name is in scope (declaring type variables is covered later). The type pattern List<E> + will result in an "invalid absolute type name" warning if no type E is in scope (declared in the default package, or + imported in the compilation unit) and no declaration of E as a type variable is in scope either. + Some simple examples of type patterns follow: + @@ -378,24 +384,20 @@ List<E> - Matches the parameterized type List<E>. If E is not - a type then an invalidAbsoluteTypeName xlint warning will be issued. + Outside of a scope in which Eis defined as a type variable, this pattern matches the + parameterized type List<E>. If E is not + a type then an invalidAbsoluteTypeName xlint warning will be issued. - - - - - <E> List<E> - - Matches the generic type List<E>. The type parameter name does not - have to match the name used in the declaration of List, but the bounds must match. - Also matches any parameterization of List that satisfies the bounds of the type variable - (for example, List<String>). + In a scope in which + E is defined as a type variable, this pattern matches the generic type List<E>. + The type parameter name does not have to match the name used in the declaration of List, + but the bounds must match. This pattern also matches any parameterization of List + that satisfies the bounds of the type variable (for example, List<String>). - - + + The *, +, and .. wildcards may be used in type patterns @@ -418,8 +420,8 @@ List<*> - Matches any parameterized Listtype (List<String>, - List<Integer> and so on). + Matches any generic or parameterized Listtype (List<String>, + List<Integer> and so on) with a single type parameter. @@ -451,27 +453,28 @@ Signature patterns - Signature patterns are extended to allow matching of generic methods and constructors, and of - members of generic types and parameterized types. - - - To match members in generic types, the type variables are declared at the start of the - signature pattern as in the following examples: + Now that we understand how to write type patterns that match generic and parameterized types, it is time to look at + how these can be utilized to match member declarations by using signature patterns. + + + To match members declared in generic types and making use of type variables defined in those types (for + example interface Foo<T> { public T doSomething(); } use a signature pattern of the form: - + .doSomething() + ]]> - - <T> T *<T>.* - - Matches a field of the type of type parameter T in any generic type with a single - unbounded type parameter. The field may be of any name. The similar looking pattern <T> T *.* is - not valid as the type parameter T must be bound in the field pattern body. - - - + + This assumes a scope in which X is declared as a type variable. As with type patterns, the name + of the type variable does not have to match the name used in the member declaration, but the bounds must match. + For example, if the interface was declared as Foo<T extends Number> then the signature + pattern would be: X Foo<X extends Number>.doSomething(). + + + - <T extends Number,S> T Util<T,S>.someFunction(List<S>) + T Util<T extends Number,S>.someFunction(List<S>) Matches the method someFunction in a generic type Util with two type parameters, the first type parameter having an upper bound of Number. @@ -480,7 +483,7 @@ - <E> LinkedList<E>.new() + LinkedList<E>.new() Matches the no-argument constructor of the generic type LinkedList. @@ -488,14 +491,27 @@ + + + Matching a field with a generic type works in the same way. For example: + + + .* + ]]> + + Matches a field of the type of type parameter T in any generic type with a single + unbounded type parameter (the pattern*<T>). The field may be of any name. + - Matching of members of parameterized types is straightforward. For example, + Matching of members of parameterized types is straightforward. For example, void List<String>.add(String) matches the add method in the - parameterized type List<String>. + parameterized type List<String>. + - To match a generic method simply omit the binding of the type variable(s) in the declaring type - pattern. For example: + To match a generic method the generic method type variable + declarations become part of the signature pattern. For example: In this section we discuss how type patterns and signature patterns matching on generic and - parameterized types, methods, and constructors can be used in pointcut expressions. We distinguish - between pointcuts that match based on static type information, and pointcuts that match based on - runtime type information (this, target, args). + parameterized types, methods, and constructors can be used in pointcut expressions. + We distinguish between pointcuts that match based on static type information, and pointcuts + that match based on runtime type information (this, target, args). + + + + First however we need to address the notion of type variables and scopes. There is a + convention in Java, but no requirement, that type variables are named with a single letter. + Likewise it is rare, but perfectly legal, to declare a type with a single character name. Given the + type pattern List<Strng>, is this a mis-spelling of the + parameterized type pattern List<String> or is it a generic type pattern + with one unbounded type variable Strng?. Alternatively, given the + type pattern List<E>, if the type E cannot be found, + is this a missing import statement or an implied type variable? There is no way for AspectJ + to disambiguate in these situations without an explicit declaration of type variable names. If + E is defined as a type variable, and Strng is not, then both + declarations can be correctly interpreted. + + + + Type Variables in Pointcut Expressions + + The type variables in scope for a pointcut primitive are declared in a type variable + list immediately following the pointcut desginator keyword. For example: + + (* Foo.*(T)) + ]]> + + matches a call to a method with any name (*) declared + by a generic type Foo with one unbounded type parameter. The method + takes one argument which is of the type of the type variable. + + In contrast, the pointcut + + .*(T)) + ]]> + + matches a call to a method with any name that takes an argument of + type T, where the target of the call is declared as the parameterized + type Foo<T>. If there is no type T in scope, an + "invalid absolute type name (T)" warning will be issued. + + + The type variables declaration following a pointcut designator permits only simple identifiers + (e.g. <S,T> and not <S extends Number>). + + A type variable declaration list can appear following any pointcut designator except + for handler (Java 5 does + not permit a generic class to be a direct or indirect subtype of Throwable + - see JLS 8.1.2), if, cflow, cflowbelow, and the annotation pointcut designators + (@args, @this, @within and so on). + + + + + Initialization and execution pointcuts + + + Recall that there is only ever one type for a generic type (e.g. List<E>) + regardless of how many different parameterizations of that type (e.g. + List<String>, List<Double>) are used within a + program. For join points that occur within a type, such as execution join points, it therefore only + makes sense to talk about execution join points for the generic type. Given the generic type + + + { + + T doSomething(T toSomeT) { + return T; + } + + } + ]]> + + + then + + + + + + execution<T>(T Foo<T>.doSomething(..)) + + matches the execution of the doSomething method in + Foo. + + + + + + execution(* Foo.doSomething(..)) + + also matches the execution of the doSomething method in + Foo. + + + + + + execution(T Foo.doSomething(..)) + + results in an "invalid absolute type name (T)" warning since T is + interpreted as a type, not a type variable. + + + + + + execution(String Foo<String>.doSomething(..)) + + results in a compilation error "no execution join points for parameterized type + Foo<String>, use a generic signature instead". + + + + + + + + Given the type declaration + + + { + + N doSomething(N toSomeN) { + return N; + } + + } + ]]> + + + then + + + + + + execution<T>(T Bar<T>.doSomething(..)) + + does not match the execution of Bar.doSomething since + the bounds of the type parameter T in the pointcut expression do + not match the bounds of the type parameter N in the type declaration. + + + + + + execution<T>(T Bar<T extends Number>.doSomething(..)) + + matches the execution of the doSomething method in + Bar. + + + + + + execution<T extends Number>(T Bar<T>.doSomething(..)) + + results in a compilation error, since type variable bounds must be specified as part + of the declaring type pattern, and not in the type variable list. + + + + + + + + If a type implements a parameterized interface, then + execution join points exist and can be matched for the parameterized interface operations within + the implementing type. For example, given the pair of types: + + + { + T greatest(List ts); + } + + public class NumberOperations implements Greatest { + public Number greatest(List numbers) { + //... + } + } + ]]> + + + then + + + .*(..)) + ]]> + + + will match the execution of the greatest method declared in + NumberOperations. However, it does not + match the execution of greatest in the program below: + + + { + T greatest(List ts); + } + + public class NumberOperations implements Greatest { + public N greatest(List numbers) { + //... + } + } + + // in some fragment of code... + NumberOperations numOps = new NumberOperations(); + numOps.greatest(numList); + ]]> + + Since there is only one generic type, NumberOperations, + which implements a generic interface. Either of the pointcut expressions + execution<T>(* Greatest<T>>.*(..)) or + execution<T>(* Greatest<T extends Number>>.*(..)) will + match the execution of greatest in this example. Recall from + chapter that a kinded pointcut primitive matches a join point if + it exactly matches one of the signatures of the join point. The signatures of the + execution join point for greatest in the example above are: + + + + + public N Greatest<N>.greatest(List<N>) + + from the declaration in the Greatest interface, and + + + + + + public N Greatest<N extends Number>.greatest(List<N>) + + from the additional bounds restriction of N in the + declaration of NumberOperations + + + + + + + + Join points for staticinitialization,initialization and + preinitialization + only ever exist on a generic type (an interface cannot define a constructor). The expression + initialization<T>(Foo<T>.new(..)) which match any initialization + join point for the generic type Foo<T>, and + staticinitialization<T>(Foo<T>) matches the static initialization + of that same type. + + + + The expression staticinitialization(List<String>) will result in a + compilation error: there is no static initialization join point for the parameterized type + List<String>. However, the expression + staticinitialization(List<String>+) is + legal, and will match the static initialization of any type that + implements List<String>. The expression + staticinitialization<T>(List<T>+) will match the static + initialization join point of any type that either extends or implements the generic + type List<T> or implements any parameterization of that + interface. + + + + + + Static scoping: within and withincode + + The within and withincode + pointcut designators both match the + execution of join points that occur within a type or a member of a type respectively. Therefore + the same considerations with respect to there only being one type for + a generic type regardless of how many parameterizations of that type are used in a program + apply. + + + The within pointcut designator can never be used in conjunction + with a simple parameterized type. So + + + + + + within<T>(Foo<T>) + + matches all join points occurring within the generic type Foo<T>, + and + + + + + + within(Foo<String>) + + results in a compilation error since there is no concept of a join point within a + parameterized type, but + + + + + + within(Foo<String>+) + + matches any join point occurring within a type that + implements Foo<String>. + + + + + + + The withincode designator is likewise normally used with a + generic type, but can be used with a parameterized interface type to match join points + arising from code lexically within the implementation of the interface methods in a type + that implements the parameterized interface. + + + + + + withincode<T>(* Foo<T>.*(..)) + + matches all join points arising from code lexically within a method of the + generic type Foo<T> + + + + + + withincode(* Foo<String>.*(..)) + + results in a compilation error if Foo is not an interface. If + Foo is an interface then it matches all join points arising from + code lexically within the implementation of the interface methods in a type that + implements Foo<String>. + + + + + + withincode(* Foo<String>+.*(..)) + + matches any join point occurring within a method of a type that + implements Foo<String>. + + + + + + + + + + Call, get and set pointcuts + + + + The call, get, and set join points can occur on the client + side (ie. outside of the type owning the member being called, accessed, or updated) or + within the type that owns the target member. The following short program demonstrates this: + + + { + + public T timeFor; + + public Foo(T aCuppa) { + timeFor = aCuppa; // set-site A + } + + public void doThis(T t) { + doThat(t); // call-site A + } + + public void doThat(T t) { + return; + } + + } + + public class Main { + public static void main(String[] args) { + Foo foos = new Foo(); + foos.doThis("b"); //call-site B + foos.doThat("c"); // call-site C + foos.timeFor = "a cuppa"; // set-site B + } + } + ]]> + + + We have annotated the three method call sites as call-site A, call-site B, and call-site C. + Call-site A is situated within the generic type Foo<T> and the call + join point has signature public void Foo<T>doThat(T). The join point + arising from call-site B is a client-side call join point and has the signatures + public void Foo<String>doThis(String) (from the static type of + foos) and + public void Foo<T>doThis(T). Likewise the call join point arising from + call-site C has the signatures + public void Foo<String>doThat(String) (from the static type of + foos) and + public void Foo<T>doThat(T). A call pointcut expression matches if the + signature pattern exactly matches one of the signatures of the call join point. + + + + The signatures for get and set join points works in a similar fashion. At set-site A in the above + example, the set join point has signature public T Foo<T>.timeFor. At + set-site B the set join point has signatures public T Foo<T>.timeFor and + public String Foo<String>.timeFor. A get or set pointcut expression + matches if the signature pattern exactly matches one of the signatures of the join point. + + + Some examples follow: + + + + + Handler + + + + Runtime type matching: this, target and args + + + - can have execution jps for parameterized interface types? .*(..)) - call(* List.*(..)) - execution(T List.*(..)) - execution(* List.*(T)) - execution( * *(..)) - execution( T *.*(T,T)) - execution( T *.*(T,T)) - execution(static T *.*(T)) call(* List.*(..)) call(* List.*(..)) call(* List.*(..)) @@ -537,7 +977,9 @@ examples with "+" ]]> - declaring pointcuts in generic classes. + + Declaring pointcuts in generic classes + @@ -678,7 +1120,8 @@ Both generic and parameterized types can be used as the parent type in a declare parents statement (as long as the resulting type hierarchy would be well-formed in accordance with Java's sub-typing rules). Generic types may also be used as the target type of a declare parents statement: - a type variable specification preceeds the type pattern in these cases. + a type variable list follows the parents keyword in these cases to declare the + type variables in scope. Some examples follow: @@ -695,7 +1138,7 @@ - declare parents: <T> org.xyz..*<T> extends Base<T> + declare parents <T>: org.xyz..*<T> extends Base<T> All generic types declared in a package beginning with org.xyz and with a single unbounded type parameter, extend the generic type Base<T>. @@ -704,7 +1147,7 @@ - declare parents: <T> org.xyz..*<T> extends Base<S> + declare parents <T>: org.xyz..*<T> extends Base<S> Results in a compilation error (unless S is a type) since S is not bound in the type pattern.