From 6b83e5b5fdcac1c886ca4633e5389221bfbcdcb4 Mon Sep 17 00:00:00 2001 From: acolyer Date: Wed, 22 Jun 2005 12:43:06 +0000 Subject: [PATCH] important updates for call, get, and set wrt. erasure and declaring type semantics --- docs/adk15ProgGuideDB/generics.xml | 242 ++++++++++++++++------------- 1 file changed, 136 insertions(+), 106 deletions(-) diff --git a/docs/adk15ProgGuideDB/generics.xml b/docs/adk15ProgGuideDB/generics.xml index 7e04bf01c..d109020f3 100644 --- a/docs/adk15ProgGuideDB/generics.xml +++ b/docs/adk15ProgGuideDB/generics.xml @@ -385,7 +385,7 @@ List<E> - Outside of a scope in which Eis defined as a type variable, this pattern matches the + Outside of a scope in which E is 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. @@ -421,7 +421,7 @@ List<*> - Matches any generic or parameterized Listtype (List<String>, + Matches any generic or parameterized List type (List<String>, List<Integer> and so on) with a single type parameter. @@ -505,11 +505,6 @@ unbounded type parameter (the pattern*<T>). The field may be of any name. - 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>. - - To match a generic method the generic method type variable declarations become part of the signature pattern. For example: @@ -555,21 +550,19 @@ list immediately following the pointcut desginator keyword. For example: (* Foo.*(T)) + get(Foo *) ]]> - 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. + matches a get join point for a field with any name (*) and of + the generic type Foo<T>. In contrast, the pointcut .*(T)) + get(Foo.*) ]]> - 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 + matches a get join point for a field with any name and of the parameterized type Foo<T>. If there is no type T in scope, an "invalid absolute type name (T)" warning will be issued. @@ -752,8 +745,8 @@ 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 + 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 @@ -811,9 +804,8 @@ 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 same considerations apply 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. The within pointcut designator can never be used in conjunction @@ -853,7 +845,7 @@ 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 + arising from code lexically within the implementation of the interface methods, in a type that implements the parameterized interface. @@ -896,9 +888,8 @@ 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: + At a call join point, the target of the call may be either a generic or + a parameterized type. The following short program demonstrates this: (T aCuppa) { + public Foo(T aCuppa) { timeFor = aCuppa; // set-site A } @@ -922,7 +913,7 @@ public class Main { public static void main(String[] args) { - Foo foos = new Foo(); + Foo foos = new Foo("tea"); foos.doThis("b"); //call-site B foos.doThat("c"); // call-site C foos.timeFor = "a cuppa"; // set-site B @@ -932,51 +923,65 @@ 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: - - + Call-site A is situated within the generic type Foo<T> and + the target of the call is of type Foo<T>. Call-sites B and C have a + target of type public void Foo<String>. However, in all three cases + the declaring type of the method is Foo<T>. + A call pointcut expression matches based on the declaring type of a method + (where specified) so all three call-sites in the above program will give rise to call join points + matched by the pointcut call<T>(* Foo<T>.do*(T)), but + none of them will be matched by the pointcut + call(* Foo<String>.do*(String)). + + + Likewise both of the set join points in the above program (arising from the lines of + code annotated "set-site A" and "set-site B" are matched by the pointcut expression + set<T>(T Foo<T>.*), but neither of them are + matched by the pointcut expression set(String Foo<String>.*). + + + + Specifying a parameterized type pattern in the declaring type pattern position of a call, get or set + pointcut expression results in a compilation error "declaring type cannot be parameterized". + The call, get, and set pointcut designators can be combined with + target to match based on the actual (possibly parameterized) target type of the receiver. + + + + A field's type may be either generic or parameterized, as illustrated in the example below. + - - call(* List<?>.*(..)) - - matches a call to any method of a List<?> (a call where the - target is declared to be a List<?>). For example: - anyList) { - return anyList.size(); // matched by call(* List.*(..)) - } + class Farmers { + private F aField; // field with a generic type + private List; // field with a generic type + private List fieldNames; // field with a parameterized type + } ]]> + + The get and set join points for a field declared with a generic type are + only matched by get and set pointcut expressions that specify a generic type pattern (or *) + for the field type. For example, get<T>(T Farmers<T>.aField) + matches a get of the field aField, whereas + get(String Farmers<String>.aField) never does (even when the + target object at the join point is of type Farmers<String>). + + + + A field with a parameterized type is matched by specifying a parameterized type + pattern (or *) for the field type. For example, set(List<String> Farmers.fieldName). + The expression set<T>(List<String> Farmers<T>.fieldNames) would + also match here (but would no longer match if the Farmers type were refactored + to be a plain (non-generic) type). + - - + call<T>(* List<T>.*(..)) matches any call to an operation defined in the generic type - List<E>. This includes calls made to List<String>, + List<E>. This includes calls where the target is of type List<String>, List<Number>, List<? super Foo> and so on. @@ -987,44 +992,51 @@ matches the get of any field defined in a generic type with one type parameter that has an upper bound of Account. The field has the type of the type parameter, and - can be of any name. This pointcut expression matches both gets of the field within the - declaring type, and also gets on parameterized instances of the type. + can be of any name. - set(Account Foo<Account>.*Account) + set(List<Account> Foo.*Account) - matches the set of a field of type Account where the target - is of type Foo<Account> and the field name ends with "Account". Does not - match sets of any "*Account" field occurring within the Foo type itself. + matches the set of a field of type List<Account> declared in + type Foo where the field name ends with "Account". - call(* List<? extends Number>.add(..)) + get(List<? extends Number> *) - matches any call to add an element to a list of type List<? extends Number>. - Does not match calls to add elements to lists of type List<Number> or + matches any get of a field of type List<? extends Number>. + Does not match gets of fields of type List<Number> or List<Double> as these are distinct types. - call(* List<Number+>.add(..)) + get(List<Number+> *) - matches any call to add an element to a list of type Number or + matches any get of a field of type List<Number> or any subclass of Number. For example, List<Number>, List<Double> List<Float>. - Does not match calls to add elements to lists of type List<? extends Number> + Does not match a get join point for a field of type List<? extends Number> as this is a distinct type. + + call(* List<?>.*(..)) + + results in a compilation error, "declaring type cannot be parameterized (List<?>)". + + + + + @@ -1081,59 +1093,77 @@ To match specific parameterizations of a generic type, simply use the type that you require the relevant object to be an instance of inside the pointcut expression. For example: - target(List<String>). + args(List<String>). + Recall that runtime tests to determine whether an object is an instance of a parameterized type are not possible due to erasure. Therefore AspectJ matching behaviour with parameterized types for this, target and args is as follows. - - If it can be statically determined that a given object will always be an instance - of the required type, then the pointcut expressions matches. For example, given a variable - bankAccounts - of type Set<BankAccount> and the pointcut expression - target(Set<BankAccount>) then any call made to - bankAccounts will be matched. - If it can be statically determined that a given object can never be an + For a parameterized type specified in an args pointcut expression: + + + If it can be statically determined that a given object will always be an instance + of the required type, then the pointcut expression matches. For example, given a method + with signature void foo(Set<BankAccount>) + and the pointcut expression + args(Set<BankAccount>) then any call or execution join point + for the method will be matched. + If it can be statically determined that a given object can never be an instance of the required type, then the pointcut expression does not match. The - expression target(List<String>)will never match a call made - using a variable of type List<Number> (it is not possible for - a type to implement two different parameterizations of the same interface). - If an object might be an instance of the required + expression args(List<String>)will never match a call or + execution join point for a method taking a single argument of type List<Number> (it is not possible for + a type to implement two different parameterizations of the same interface). + If an object might be an instance of the required type in some circumstances but not in others, then since it is not possible to perform the runtime test, AspectJ deems the pointcut expression to match, but issues an unchecked warning. This is analogous to the behaviour of the Java compiler when - converting between raw and parameterized types. Given a variable of type - List<? extends Number> and a call join point with - target pointcut expression target(List<Double>) then - the expression matches but with an unchecked warning. The warning can be suppressed + converting between raw and parameterized types. Given a method that takes a single argument of type + List<? extends Number> and + pointcut expression args(List<Double>) then + the expression matches call and execution join points for the method, but with an unchecked warning. The warning can be suppressed by annotating the associated advice with either @SuppressAjWarnings - or @SuppressAjWarnings("unchecked"). - + or @SuppressAjWarnings("unchecked"). + When using a parameterized type with the - this pointcut designator then a joinpoint is unambiguously - matched if and only if one or more of the following conditions hold: + this pointcut designator then a joinpoint is + matched if and only if at least one of the following conditions hold: - - the runtime type of the this object extends or - implements the parameterized type. For example, - class Foo implements List<String> will match - this(List<String>). - + + + the runtime type of the this object extends or + implements the parameterized type. For example, an object with runtime type Foo, + defined as + class Foo implements List<String>, will match + this(List<String>). + The parameterized "this" type is given using a generics wildcard in the pointcut expression, and the bounds of the generic runtime type of this are such that all valid parameterizations are matched by the wildcard. For example, the pointcut expression this(List<? extends Number>) will match a this - object of type class Foo<N extends Number> implements List<N>, - but not an object of type class Foo<N> implements List<N>. - - + object of type class Foo<N extends Number> implements List<N>. + If some parameterization may be matched by the wildcard (for + example an object of type class Foo<N> implements List<N>) the pointcut + will match but with an unchecked warning. + + + + + Using a parameterized type with the target pointcut designator is not supported in + the 1.5.0 release of AspectJ 5. If a future release of AspectJ supports this feature, the matching rules + will be the same as those described for args. The reason for the limitation is that + erasure makes it impossible in the general case to determine the parameterized target type at a call, get, or set + join point from Java bytecodes (only the raw type information is available). If a class file has been compiled + with variable debug information ("-g" or "-g:vars") then parameterized type information can + be recovered using a combination of data flow analysis and the local variable type table (potentially expensive). A more + efficient implementation would be possible for class files compiled from source by ajc. + You've already seen some examples of using the generic wildcard ? @@ -1142,13 +1172,13 @@ specifying an acceptable range of parameterized types to match. When used in the binding form, the same restrictions on operations permitted on the bound variable apply as when a method declares a parameter with a wildcard type. For example, in the advice below, it is - a compilation error to attemp to add an element into the list aList. + a compilation error to attempt to add an element into the list aList. aList) : execution(* org.xyz.Foo.*(..)) && args(aList) { - aList.add(new Double(5.0d)); // Compilation error on this line + aList.add(5.0d); // Compilation error on this line } ]]> -- 2.39.5