diff options
author | acolyer <acolyer> | 2005-08-17 16:33:50 +0000 |
---|---|---|
committer | acolyer <acolyer> | 2005-08-17 16:33:50 +0000 |
commit | e8f7b99508f0e44f756c465d710d7536f34c3710 (patch) | |
tree | df09178e94d560119c76808108447aaef69cfeaa /docs/adk15ProgGuideDB/generics.xml | |
parent | 779fa790d491b486fe0deb2616076910767f5d53 (diff) | |
download | aspectj-e8f7b99508f0e44f756c465d710d7536f34c3710.tar.gz aspectj-e8f7b99508f0e44f756c465d710d7536f34c3710.zip |
pretty much a complete rewrite, this time matching the actual implementation!
Diffstat (limited to 'docs/adk15ProgGuideDB/generics.xml')
-rw-r--r-- | docs/adk15ProgGuideDB/generics.xml | 1687 |
1 files changed, 673 insertions, 1014 deletions
diff --git a/docs/adk15ProgGuideDB/generics.xml b/docs/adk15ProgGuideDB/generics.xml index d109020f3..1514d7ded 100644 --- a/docs/adk15ProgGuideDB/generics.xml +++ b/docs/adk15ProgGuideDB/generics.xml @@ -313,916 +313,599 @@ </para> <sect2> - <title>Matching generic and parameterized types in type patterns</title> - - <para> - 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. - </para> - - <para> - The type pattern <literal>"Foo"</literal> matches all types named <literal>Foo</literal>, whether they - be simple types, generic types, or parameterized types. So for example, <literal>Foo</literal>, - <literal>Foo<T></literal>, and <literal>Foo<String></literal>will all be matched. - </para> - - <para> - AspectJ 5 also extends the specification of type patterns to allow explicit matching of generic and parameterized - types by including one or more type parameter patterns inside angle braces (<literal>< ></literal>) immediately - after the type pattern. For example, <literal>List<String></literal> - </para> - - <programlisting><![CDATA[ - TypePattern := SimpleTypePattern | - '!' TypePattern | - '(' AnnotationPattern? TypePattern ')' - TypePattern '&&' TypePattern | - TypePattern '||' TypePattern | - TypePattern '<' TypeParameterPatternList '>' - - TypeParameterPatternList ::= TypeParameterPattern (',' TypeParameterPattern)* - - TypeParameterPattern ::= TypePattern | - '?' TypeBoundPattern? - - TypeBoundPattern ::= 'extends' TypePattern AdditionalBoundPatternList? | - 'super' TypePattern AdditionalBoundPatternList? - - AdditionalBoundPatternList ::= AdditionalBoundPattern AdditionalBoundPatternList | - AdditionalBoundPattern - - AdditionalBoundPattern ::= '&' TypePattern - - ]]></programlisting> - - <para> - A simple identifier (such as <literal>String</literal>) 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 <literal>List<E></literal> - will result in an "invalid absolute type name" warning if no type <literal>E</literal> is in scope (declared in the default package, or - imported in the compilation unit) and no declaration of <literal>E</literal> as a type variable is in scope either. - </para> - - <para>Some simple examples of type patterns follow:</para> - - <variablelist> - - <varlistentry> - <term>List<String></term> - <listitem> - <para>Matches the parameterized type <literal>List<String></literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>List<? extends Number></term> - <listitem> - <para>Matches the parameterized type <literal>List<? extends Number></literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>List<E></term> - <listitem> - <para>Outside of a scope in which <literal>E</literal> is defined as a type variable, this pattern matches the - parameterized type <literal>List<E></literal>. If <literal>E</literal> is not - a type then an <literal>invalidAbsoluteTypeName</literal> xlint warning will be issued. - </para> - <para>In a scope in which - <literal>E</literal> is defined as a type variable, this pattern matches the generic type <literal>List<E></literal>. - The type parameter name does not have to match the name used in the declaration of <literal>List</literal>, - but the bounds must match. This pattern <emphasis>also</emphasis> matches any parameterization of <literal>List</literal> - that satisfies the bounds of the type variable (for example, <literal>List<String></literal>). - </para> - </listitem> - </varlistentry> - - </variablelist> - - <para> - The <literal>*</literal>, <literal>+</literal>, and <literal>..</literal> wildcards may be used in type patterns - matching against generic and parameterized types (just as in any other type pattern). The <literal>+</literal> - wildcard matches all subtypes. Recalling the discussion on subtypes and supertypes in the previous section, note - that the pattern <literal>List<Number>+</literal> will match <literal>List<Number></literal> and - <literal>LinkedList<Number></literal>, but not <literal>List<Double></literal>. To match lists of - any number type use the pattern <literal>List<Number+></literal> which will match - <literal>List<Number></literal>, <literal>List<Double></literal>, <literal>List<Float></literal> - and so on. - </para> - - <para> - The generics wildcard <literal>?</literal> is considered part of the signature of a parameterized type, and - is <emphasis>not</emphasis> used as an AspectJ wildcard in type matching. For example: - </para> - - <variablelist> - - <varlistentry> - <term>List<*></term> - <listitem> - <para>Matches any generic or parameterized <literal>List</literal> type (<literal>List<String></literal>, - <literal>List<Integer></literal> and so on) with a single type parameter. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>List<?></term> - <listitem> - <para>Matches the parameterized type <literal>List<?></literal> (and does - <emphasis>not</emphasis> match <literal>List<String></literal>, - <literal>List<Integer></literal> and so on) - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>List<? extends Number+></term> - <listitem> - <para>Matches <literal>List<? extends Number></literal>, <literal>List<? extends Double></literal>, - and so on, but does not match <literal>List<Double></literal>. - </para> - </listitem> - </varlistentry> - - </variablelist> - - </sect2> - - <sect2> - <title>Signature patterns</title> + <title>Matching generic and parameterized types in pointcut expressions</title> <para> - 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. - </para> - - <para>To match members declared in generic types and making use of type variables defined in those types (for - example <literal>interface Foo<T> { public T doSomething(); }</literal> use a signature pattern of the form:</para> - - <programlisting><![CDATA[ - X Foo<X>.doSomething() - ]]></programlisting> - - <para> - This assumes a scope in which <literal>X</literal> 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 <literal>Foo<T extends Number></literal> then the signature - pattern would be: <literal>X Foo<X extends Number>.doSomething()</literal>. - </para> - - <variablelist> - - <varlistentry> - <term>T Util<T extends Number,S>.someFunction(List<S>)</term> - <listitem> - <para>Matches the method <literal>someFunction</literal> in a generic type <literal>Util</literal> with - two type parameters, the first type parameter having an upper bound of <literal>Number</literal>. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>LinkedList<E>.new()</term> - <listitem> - <para>Matches the no-argument constructor of the generic type <literal>LinkedList</literal>. - </para> - </listitem> - </varlistentry> - - </variablelist> - - <para> - Matching a field with a generic type works in the same way. For example: + The simplest way to work with generic and parameterized types in pointcut expressions and type patterns + is simply to use the raw type name. For example, the type pattern <literal>List</literal> will match + the generic type <literal>List<E></literal> and any parameterization of that type + (<literal>List<String>, List<?>, List<? extends Number></literal> and so on. This + ensures that pointcuts written in existing code that is not generics-aware will continue to work as + expected in AspectJ 5. It is also the recommended way to match against generic and parameterized types + in AspectJ 5 unless you explicitly wish to narrow matches to certain parameterizations of a generic type. </para> + + <para>Generic methods and constructors, and members defined in generic types, may use type variables + as part of their signature. For example:</para> - <programlisting><![CDATA[ - T *<T>.* + <programlisting><![CDATA[ + public class Utils { + + /** static generic method */ + static <T> T first(List<T> ts) { ... } + + /** instance generic method */ + <T extends Number> T max(T t1, T t2) { ... } + + } + + public class G<T> { + + // field with parameterized type + T myData; + + // method with parameterized return type + public List<T> getAllDataItems() {...} + + } ]]></programlisting> - - <para>Matches a field of the type of type parameter <literal>T</literal> in any generic type with a single - unbounded type parameter (the pattern<literal>*<T></literal>). The field may be of any name. - </para> <para> - To match a generic <emphasis>method</emphasis> the generic method type variable - declarations become part of the signature pattern. For example: + AspectJ 5 does not allow the use of type variables in pointcut expressions and type patterns. Instead, members that + use type parameters as part of their signature are matched by their <emphasis>erasure</emphasis>. Java 5 defines the + rules for determing the erasure of a type as follows. </para> - - <programlisting><![CDATA[ - <T> List<T> *.favourites(List<T>) - ]]></programlisting> - - <para>matches a generic method <literal>favourites</literal> declared in any type. To match a - static generic method simply include the <literal>static</literal> modifier in the type pattern.</para> + + <para>Let <literal>|T|</literal> represent the erasure of some type <literal>T</literal>. Then:</para> - </sect2> - - <sect2> - <title>Pointcuts</title> - - <para> - 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 (<literal>this, target, args</literal>). - </para> - - <para> - 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 <literal>List<Strng></literal>, is this a mis-spelling of the - parameterized type pattern <literal>List<String></literal> or is it a generic type pattern - with one unbounded type variable <literal>Strng</literal>?. Alternatively, given the - type pattern <literal>List<E></literal>, if the type <literal>E</literal> 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 - <literal>E</literal> is defined as a type variable, and <literal>Strng</literal> is not, then both - declarations can be correctly interpreted. - </para> + <simplelist> + <member>The erasure of a parameterized type <literal>T<T1,...,Tn></literal> is <literal>|T|</literal>. + For example, the erasure of <literal>List<String></literal> is <literal>List</literal>.</member> + + <member>The erasure of a nested type <literal>T.C</literal> is <literal>|T|.C</literal>. For example, + the erasure of the nested type <literal>Foo<T>.Bar</literal> is <literal>Foo.Bar</literal>.</member> + + <member>The erasure of an array type <literal>T[]</literal> is <literal>|T|[]</literal>. For example, + the erasure of <literal>List<String>[]</literal> is <literal>List[]</literal>.</member> + + <member>The erasure of a type variable is its leftmost bound. For example, the erasure of a + type variable <literal>P</literal> is <literal>Object</literal>, and the erasure of a type + variable <literal>N extends Number</literal> is <literal>Number</literal>.</member> + + <member>The erasure of every other type is the type itself</member> + </simplelist> + + <!-- see tests/java5/generics/ajdk/ErasureMatching.aj --> + <para>Applying these rules to the earlier examples, we find that the methods defined in <literal>Utils</literal> + can be matched by a signature pattern matching <literal>static Object Utils.first(List)</literal> and + <literal>Number Utils.max(Number, Number)</literal> respectively. The members of the generic type + <literal>G</literal> can be matched by a signature pattern matching <literal>Object G.myData</literal> and + <literal>public List G.getAllDataItems()</literal> respectively.</para> + + <sect3> + <title>Restricting matching using parameterized types</title> + + <para>Pointcut matching can be further restricted to match only given parameterizations of parameter types (methods and constructors), return + types (methods) and field types (fields). This is achieved by specifying a parameterized type pattern at the appropriate point + in the signature pattern. For example, given the class <literal>Foo</literal>:</para> - <sect3> - <title>Type Variables in Pointcut Expressions</title> - - <para>The type variables in scope for a pointcut primitive are declared in a type variable - list immediately following the pointcut desginator keyword. For example:</para> - <programlisting><![CDATA[ - get<T>(Foo<T> *) + public class Foo { + + List<String> myStrings; + List<Float> myFloats; + + public List<String> getStrings() { return myStrings; } + public List<Float> getFloats() { return myFloats; } + + public void addStrings(List<String> evenMoreStrings) { + myStrings.addAll(evenMoreStrings); + } + + } ]]></programlisting> - - <para>matches a get join point for a field with any name (<literal>*</literal>) and of - the generic type <literal>Foo<T></literal>.</para> + + <!-- see tests/java5/generics/ajdk/SimpleParameterizedTypeExamples.aj --> - <para>In contrast, the pointcut</para> + <para>Then a <literal>get</literal> join point for the field <literal>myStrings</literal> can be matched by the + pointcut <literal>get(List Foo.myStrings)</literal> and by the pointcut <literal>get(List<String> Foo.myStrings)</literal>, + but <emphasis>not</emphasis> by the pointcut <literal>get(List<Number> *)</literal>.</para> - <programlisting><![CDATA[ - get(Foo<T>.*) - ]]></programlisting> + <para>A <literal>get</literal> join point for the field <literal>myFloats</literal> can be matched by the + pointcut <literal>get(List Foo.myFloats)</literal>, the pointcut <literal>get(List<Float> *)</literal>, + and the pointcut <literal>get(List<Number+> *)</literal>. This last example shows how AspectJ type + patterns can be used to match type parameters types just like any other type. The pointcut + <literal>get(List<Double> *)</literal> does <emphasis>not</emphasis> match.</para> - <para>matches a get join point for a field with any name and of the parameterized - type <literal>Foo<T></literal>. If there is no type <literal>T</literal> in scope, an - "invalid absolute type name (T)" warning will be issued.</para> + <para>The execution of the methods <literal>getStrings</literal> and <literal>getFloats</literal> can be + matched by the pointcut expression <literal>execution(List get*(..))</literal>, and the pointcut + expression <literal>execution(List<*> get*(..))</literal>, but only <literal>getStrings</literal> + is matched by <literal>execution(List<String> get*(..))</literal> and only <literal>getFloats</literal> + is matched by <literal>execution(List<Number+> get*(..))</literal></para> - <para> - The type variables declaration following a pointcut designator permits only simple identifiers - (e.g. <literal><S,T></literal> and not <literal><S extends Number></literal>). + <para>A call to the method <literal>addStrings</literal> can be matched by the pointcut expression + <literal>call(* addStrings(List))</literal> and by the expression <literal>call(* addStrings(List<String>))</literal>, + but <emphasis>not</emphasis> by the expression <literal>call(* addStrings(List<Number>))</literal>. </para> - <para>A type variable declaration list can appear following any pointcut designator except - for <literal>handler</literal> (Java 5 does - not permit a generic class to be a direct or indirect subtype of <literal>Throwable</literal> - - see JLS 8.1.2), the dynamic pointcuts <literal>this, target, args, if, cflow, cflowbelow</literal>, - and the annotation pointcut designators - (<literal>@args, @this, @within</literal> and so on).</para> + <para>Remember that any type variable reference in a generic member is + <emphasis>always</emphasis> matched by its erasure. Thus given the following + example:</para> - </sect3> - - <sect3> - <title>Initialization and execution pointcuts</title> + <programlisting><![CDATA[ + class G<T> { + + List<T> foo(List<String ls) { return null; } + + } + ]]></programlisting> - <para> - Recall that there is only ever one type for a generic type (e.g. <literal>List<E></literal>) - regardless of how many different parameterizations of that type (e.g. - <literal>List<String></literal>, <literal>List<Double></literal>) 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 - </para> - + <!-- see tests/java5/generics/ajdk/MixedParameterizedAndTypeVariables.aj --> + <para>The execution of <literal>foo</literal> can be matched by + <literal>execution(List foo(List))</literal>, + <literal>execution(List foo(List<String>>))</literal>, and + <literal>execution(* foo(List<String<))</literal>but + <emphasis>not</emphasis> by <literal>execution(List<Object> foo(List<String>>)</literal> + since the erasure of <literal>List<T></literal> is <literal>List</literal> + and not <literal>List<Object></literal>. + </para> + + </sect3> + + <sect3> + <title>Generic wildcards and signature matching</title> + + <para> + When it comes to signature matching, a type parameterized using a generic wildcard is a distinct type. + For example, <literal>List<?></literal> is a very different type to <literal>List<String></literal>, + even though a variable of type <literal>List<String></literal> can be assigned to a variable of + type <literal>List<?></literal>. Given the methods: + </para> + <programlisting><![CDATA[ - public class Foo<T> { + class C { + + public void foo(List<? extends Number> listOfSomeNumberType) {} - T doSomething(T toSomeT) { - return T; - } - - } + public void bar(List<?> listOfSomeType) {} + + public void goo(List<Double> listOfDoubles) {} + + } ]]></programlisting> - - <para> - then - </para> - - <variablelist> + + <!-- see java5/generics/ajdk/SignatureWildcards.aj --> + + <variablelist> <varlistentry> - <term>execution<T>(T Foo<T>.doSomething(..))</term> + <term>execution(* C.*(List))</term> <listitem> - <para>matches the execution of the <literal>doSomething</literal> method in - <literal>Foo</literal>. + <para>Matches an execution join point for any of the three methods. </para> </listitem> </varlistentry> <varlistentry> - <term>execution(* Foo.doSomething(..))</term> + <term>execution(* C.*(List<? extends Number>))</term> <listitem> - <para>also matches the execution of the <literal>doSomething</literal> method in - <literal>Foo</literal>. + <para>matches only the + execution of <literal>foo</literal>, and <emphasis>not</emphasis> the execution + of <literal>goo</literal> since <literal>List<? extends Number></literal> and + <literal>List<Double></literal> are distinct types. </para> </listitem> </varlistentry> - + <varlistentry> - <term>execution(T Foo.doSomething(..))</term> + <term>execution(* C.*(List<?>))</term> <listitem> - <para>results in an "invalid absolute type name (T)" warning since <literal>T</literal> is - interpreted as a type, not a type variable. + <para>matches only the execution of <literal>bar</literal>. </para> </listitem> - </varlistentry> - + </varlistentry> + <varlistentry> - <term>execution(String Foo<String>.doSomething(..))</term> + <term>execution(* C.*(List<? extends Object+>))</term> <listitem> - <para>results in a compilation error "no execution join points for parameterized type - Foo<String>, use a generic signature instead". + <para>matches both the execution of <literal>foo</literal> and the execution of <literal>bar</literal> + since the upper bound of <literal>List<?></literal> is implicitly <literal>Object</literal>. </para> </listitem> - </varlistentry> - - </variablelist> - - <para> - Given the type declaration - </para> + </varlistentry> + + </variablelist> + + </sect3> - <programlisting><![CDATA[ - public class Bar<N extends Number> { - - N doSomething(N toSomeN) { - return N; - } + <sect3> + <title>Treatment of bridge methods</title> - } - ]]></programlisting> - - <para> - then - </para> - - <variablelist> - - <varlistentry> - <term>execution<T>(T Bar<T>.doSomething(..))</term> - <listitem> - <para>does not match the execution of <literal>Bar.doSomething</literal> since - the bounds of the type parameter <literal>T</literal> in the pointcut expression do - not match the bounds of the type parameter <literal>N</literal> in the type declaration. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>execution<T>(T Bar<T extends Number>.doSomething(..))</term> - <listitem> - <para>matches the execution of the <literal>doSomething</literal> method in - <literal>Bar</literal>. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>execution<T extends Number>(T Bar<T>.doSomething(..))</term> - <listitem> - <para>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. - </para> - </listitem> - </varlistentry> + <para>Under certain circumstances a Java 5 compiler is required to create <emphasis>bridge + methods</emphasis> that support the compilation of programs using raw types. Consider the types</para> + + <programlisting><![CDATA[ + class Generic<T> { + + public T foo(T someObject) { + return someObject; + } + + } - </variablelist> - - <para> - If a type implements a <emphasis>parameterized</emphasis> 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: - </para> - - <programlisting><![CDATA[ - public interface Greatest<T> { - T greatest(List<T> ts); - } - - public class NumberOperations implements Greatest<Number> { - public Number greatest(List<Number> numbers) { - //... - } - } + class SubGeneric<N extends Number> extends Generic<N> { + + public N foo(N someNumber) { + return someNumber; + } + + } ]]></programlisting> - - <para> - then - </para> - - <programlisting><![CDATA[ - execution(* Greatest<Number>.*(..)) + + <para>The class <literal>SubGeneric</literal> extends <literal>Generic</literal> + and overrides the method <literal>foo</literal>. Since the upper bound of the type variable + <literal>N</literal> in <literal>SubGeneric</literal> is different to the upper bound of + the type variable <literal>T</literal> in <literal>Generic</literal>, the method <literal>foo</literal> + in <literal>SubGeneric</literal> has a different erasure to the method <literal>foo</literal> + in <literal>Generic</literal>. This is an example of a case where a Java 5 compiler will create + a <emphasis>bridge method</emphasis> in <literal>SubGeneric</literal>. Although you never see it, + the bridge method will look something like this:</para> + + <programlisting><![CDATA[ + public Object foo(Object arg) { + Number n = (Number) arg; // "bridge" to the signature defined in this type + return foo(n); + } ]]></programlisting> - - <para> - will match the execution of the <literal>greatest</literal> method declared in - <literal>NumberOperations</literal>. However, it <emphasis>does not</emphasis> - match the execution of <literal>greatest</literal> in the program below: - </para> - - <programlisting><![CDATA[ - public interface Greatest<T> { - T greatest(List<T> ts); - } - - public class NumberOperations<N extends Number> implements Greatest<N> { - public N greatest(List<N> numbers) { - //... - } - } - - // in some fragment of code... - NumberOperations<Number> numOps = new NumberOperations<Number>(); - numOps.greatest(numList); + + <!-- see java5/generics/ajdk/BridgeMethodExamples.aj --> + <para>Bridge methods are synthetic artefacts generated as a result of a particular compilation strategy and + have no execution join points in AspectJ 5. So the pointcut <literal>execution(Object SubGeneric.foo(Object))</literal> + does not match anything. (The pointcut <literal>execution(Object Generic.foo(Object))</literal> matches the + execution of <literal>foo</literal> in both <literal>Generic</literal> and <literal>SubGeneric</literal> since + both are implementations of <literal>Generic.foo</literal>). + </para> + + <para>It <emphasis>is</emphasis> possible to <emphasis>call</emphasis> a bridge method as the following short + code snippet demonstrates. Such a call <emphasis>does</emphasis> result in a call join point for the call to + the method. + </para> + + <programlisting><![CDATA[ + SubGeneric rawType = new SubGeneric(); + rawType.foo("hi"); // call to bridge method (will result in a runtime failure in this case) + Object n = new Integer(5); + rawType.foo(n); // call to bridge method that would succeed at runtime ]]></programlisting> + + </sect3> - <para>Since there is only one generic type, <literal>NumberOperations</literal>, - which implements a generic interface. Either of the pointcut expressions - <literal>execution<T>(* Greatest<T>.*(..))</literal> or - <literal>execution<T>(* Greatest<T extends Number>.*(..))</literal> will - match the execution of <literal>greatest</literal> in this example. Recall from - chapter <xref linkend="jpsigs"/> 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 <literal>greatest</literal> in the example above are:</para> - - <variablelist> - - <varlistentry> - <term>public N Greatest<N>.greatest(List<N>)</term> - <listitem> - <para>from the declaration in the <literal>Greatest</literal> interface, and - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>public N Greatest<N extends Number>.greatest(List<N>)</term> - <listitem> - <para>from the additional bounds restriction of <literal>N</literal> in the - declaration of <literal>NumberOperations</literal> - </para> - </listitem> - </varlistentry> - - </variablelist> - - <para> - Join points for <literal>staticinitialization</literal>,<literal>initialization</literal> and - <literal>preinitialization</literal> - only ever exist on a generic type (an interface cannot define a constructor). The expression - <literal>initialization<T>(Foo<T>.new(..))</literal> which match any initialization - join point for the generic type <literal>Foo<T></literal>, and - <literal>staticinitialization<T>(Foo<T>)</literal> matches the static initialization - of that same type. - </para> - - <para> - The expression <literal>staticinitialization(List<String>)</literal> will result in a - compilation error: there is no static initialization join point for the parameterized type - <literal>List<String></literal>. However, the expression - <literal>staticinitialization(List<String>+)</literal> <emphasis>is</emphasis> - legal, and will match the static initialization of any type that - <literal>implements List<String></literal>. The expression - <literal>staticinitialization<T>(List<T>+)</literal> will match the static - initialization join point of any type that either extends or implements the generic - type <literal>List<T></literal> or implements any parameterization of that - interface. - </para> - - </sect3> + <sect3> + <title>Runtime type matching with this(), target() and args()</title> + + <para>The <literal>this()</literal>, <literal>target()</literal>, and + <literal>args()</literal> pointcut expressions all match based on the runtime + type of their arguments. Because Java 5 implements generics using erasure, it is not + possible to ask at runtime whether an object is an instance of a given parameterization of a type + (only whether or not it is an instance of the erasure of that parameterized type). Therefore + AspectJ 5 does not support the use of parameterized types with the <literal>this()</literal> and + <literal>target()</literal> pointcuts. Parameterized types may however be used in conjunction with + <literal>args()</literal>. Consider the following class + </para> - <sect3> - <title>Static scoping: within and withincode</title> - - <para>The <literal>within</literal> and <literal>withincode</literal> - 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 apply with respect to there only being <literal>one</literal> type for - a generic type regardless of how many parameterizations of that type are used in a program. - </para> + <programlisting><![CDATA[ + public class C { + + public void foo(List<String> listOfStrings) {} + + public void bar(List<Double> listOfDoubles) {} + + public void goo(List<? extends Number> listOfSomeNumberType) {} - <para>The <literal>within</literal> pointcut designator can never be used in conjunction - with a simple parameterized type. So - </para> - - <variablelist> - - <varlistentry> - <term>within<T>(Foo<T>)</term> - <listitem> - <para>matches all join points occurring within the generic type <literal>Foo<T></literal>, - and - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>within(Foo<String>)</term> - <listitem> - <para>results in a compilation error since there is no concept of a join point within a - parameterized type, but - </para> - </listitem> - </varlistentry> + } + ]]></programlisting> + + <!-- see java5/generics/ajdk/ArgsExamples.aj --> + + <variablelist> + <varlistentry> + <term>args(List)</term> + <listitem> + <para>will match an execution or call join point for any of + these methods + </para> + </listitem> + </varlistentry> + <varlistentry> - <term>within(Foo<String>+)</term> - <listitem> - <para>matches any join point occurring within a type that - <literal>implements Foo<String></literal>. - </para> - </listitem> - </varlistentry> - - </variablelist> + <term>args(List<String>)</term> + <listitem> + <para>will match an execution + or call join point for <literal>foo</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>args(List<Double>)</term> + <listitem> + <para>matches an execution or call join point for <literal>bar</literal>, and <emphasis>may</emphasis> match + at an execution or call join point for <literal>goo</literal> since it is legitimate to pass an + object of type <literal>List<Double></literal> to a method expecting a <literal>List<? extends Number></literal>. + </para> + <para> + In this situation a runtime test would normally be applied to ascertain whether or not the argument + was indeed an instance of the required type. However, in the case of parameterized types such a test is not + possible and therefore AspectJ 5 considers this a match, but issues an <emphasis>unchecked</emphasis> warning. + For example, compiling the aspect <literal>A</literal> below with the class <literal>C</literal> produces the + compilation warning: "unchecked match of List<Double> with List<? extends Number> when argument is + an instance of List at join point method-execution(void C.goo(List<? extends Number>)) [Xlint:uncheckedArgument]"; + </para> + </listitem> + </varlistentry> + + </variablelist> + + <programlisting><![CDATA[ + public aspect A { + + before(List<Double> listOfDoubles) : execution(* C.*(..)) && args(listOfDoubles) { + for (Double d : listOfDoubles) { + // do something + } + } - <para>The <literal>withincode</literal> 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. - </para> - - <variablelist> - - <varlistentry> - <term>withincode<T>(* Foo<T>.*(..))</term> - <listitem> - <para>matches all join points arising from code lexically within a method of the - generic type <literal>Foo<T></literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>withincode(* Foo<String>.*(..))</term> - <listitem> - <para>results in a compilation error if <literal>Foo</literal> is not an interface. If - <literal>Foo</literal> 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 <literal>Foo<String></literal>. + } + ]]></programlisting> + + <para>Like all Lint messages, the <literal>uncheckedArgument</literal> warning can be + configured in severity from the default warning level to error or even ignore if preferred. + In addition, AspectJ 5 offers the annotation <literal>@SuppressAjWarnings</literal> which is + the AspectJ equivalent of Java's <literal>@SuppressWarnings</literal> annotation. If the + advice is annotated with <literal>@SuppressWarnings</literal> then <emphasis>all</emphasis> + lint warnings issued during matching of pointcut associated with the advice will be + suppressed. To suppress just an <literal>uncheckedArgument</literal> warning, use the + annotation <literal>@SuppressWarnings("uncheckedArgument")</literal> as in the following + examples: </para> - </listitem> - </varlistentry> - <varlistentry> - <term>withincode(* Foo<String>+.*(..))</term> - <listitem> - <para>matches any join point occurring within a method of a type that - <literal>implements Foo<String></literal>. - </para> - </listitem> - </varlistentry> + <programlisting><![CDATA[ + import org.aspectj.lang.annotation.SuppressAjWarnings + public aspect A { - </variablelist> + @SuppressAjWarnings // will not see *any* lint warnings for this advice + before(List<Double> listOfDoubles) : execution(* C.*(..)) && args(listOfDoubles) { + for (Double d : listOfDoubles) { + // do something + } + } + + @SuppressAjWarnings("uncheckedArgument") // will not see *any* lint warnings for this advice + before(List<Double> listOfDoubles) : execution(* C.*(..)) && args(listOfDoubles) { + for (Double d : listOfDoubles) { + // do something + } + } - </sect3> - - <sect3> - <title>Call, get and set pointcuts</title> - - <para> - At a <literal>call</literal> join point, the target of the call may be either a generic or - a parameterized type. The following short program demonstrates this: - </para> - - <programlisting><![CDATA[ - public class Foo<T> { - - 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; - } + } + ]]></programlisting> - } - - public class Main { - public static void main(String[] args) { - Foo<String> foos = new Foo<String>("tea"); - foos.doThis("b"); //call-site B - foos.doThat("c"); // call-site C - foos.timeFor = "a cuppa"; // set-site B - } - } + <para> + The safest way to deal with <literal>uncheckedArgument</literal> warnings however is to restrict the pointcut + to match only at those join points where the argument is guaranteed to match. This is achieved by combining + <literal>args</literal> with a <literal>call</literal> or <literal>execution</literal> signature matching + pointcut. In the following example the advice will match the execution of <literal>bar</literal> but not + of <literal>goo</literal> since the signature of <literal>goo</literal> is not matched by the execution pointcut + expression. + </para> + + <programlisting><![CDATA[ + public aspect A { + + before(List<Double> listOfDoubles) : execution(* C.*(List<Double>)) && args(listOfDoubles) { + for (Double d : listOfDoubles) { + // do something + } + } + + } ]]></programlisting> - - <para> - 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 <literal>Foo<T></literal> and - the target of the call is of type <literal>Foo<T></literal>. Call-sites B and C have a - target of type <literal>public void Foo<String></literal>. However, in all three cases - the <emphasis>declaring type</emphasis> of the method is <literal>Foo<T></literal>. - A <literal>call</literal> 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 <literal>call<T>(* Foo<T>.do*(T))</literal>, but - <emphasis>none of them</emphasis> will be matched by the pointcut - <literal>call(* Foo<String>.do*(String))</literal>. - </para> - <para>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 - <literal>set<T>(T Foo<T>.*)</literal>, but <emphasis>neither of them</emphasis> are - matched by the pointcut expression <literal>set(String Foo<String>.*)</literal>. - </para> - - <para> - 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 <literal>call, get</literal>, and <literal>set</literal> pointcut designators can be combined with - <literal>target</literal> to match based on the actual (possibly parameterized) target type of the receiver. - </para> - - <para> - A field's type may be either generic or parameterized, as illustrated in the example below. - </para> + <para>Generic wildcards can be used in args type patterns, and matching follows regular Java 5 assignability rules. For + example, <literal>args(List<?>)</literal> will match a list argument of any type, and + <literal>args(List<? extends Number>)</literal> will match an argument of type + <literal>List<Number>, List<Double>, List<Float></literal> and so on. Where a match cannot be + fully statically determined, the compiler will once more issue an <literal>uncheckedArgument</literal> warning. + </para> - <programlisting><![CDATA[ - class Farmers<F> { - private F aField; // field with a generic type - private List<F>; // field with a generic type - private List<String> fieldNames; // field with a parameterized type - } + <para>Consider the following program:</para> + + <programlisting><![CDATA[ + public class C { + + public static void main(String[] args) { + C c = new C(); + List<String> ls = new ArrayList<String>(); + List<Double> ld = new ArrayList<Double>(); + c.foo("hi"); + c.foo(ls); + c.foo(ld); + } + + public void foo(Object anObject) {} + } + + aspect A { + before(List<? extends Number> aListOfSomeNumberType) + : call(* foo(..)) && args(aListOfSomeNumberType) { + // process list... + } + } ]]></programlisting> - <para>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 <literal>*</literal>) - for the field type. For example, <literal>get<T>(T Farmers<T>.aField)</literal> - matches a get of the field <literal>aField</literal>, whereas - <literal>get(String Farmers<String>.aField)</literal> never does (even when the - target object at the join point is of type <literal>Farmers<String></literal>). - </para> + <!-- see java5/generics/ajdk/WildcardArgsExamples.aj --> - <para> - A field with a parameterized type is matched by specifying a parameterized type - pattern (or <literal>*</literal>) for the field type. For example, <literal>set(List<String> Farmers.fieldName)</literal>. - The expression <literal>set<T>(List<String> Farmers<T>.fieldNames)</literal> would - also match here (but would no longer match if the <literal>Farmers</literal> type were refactored - to be a plain (non-generic) type). - </para> - - <variablelist> - - <varlistentry> - <term>call<T>(* List<T>.*(..))</term> - <listitem> - <para>matches any call to an operation defined in the generic type - <literal>List<E></literal>. This includes calls where the target is of type <literal>List<String></literal>, - <literal>List<Number></literal>, <literal>List<? super Foo></literal> and so on. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>get<T>(T *<T extends Account>.*)</term> - <listitem> - <para>matches the get of any field defined in a generic type with one type parameter that has - an upper bound of <literal>Account</literal>. The field has the type of the type parameter, and - can be of any name. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>set(List<Account> Foo.*Account)</term> - <listitem> - <para>matches the set of a field of type <literal>List<Account></literal> declared in - type <literal>Foo</literal> where the field name ends with "Account". - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>get(List<? extends Number> *)</term> - <listitem> - <para>matches any get of a field of type <literal>List<? extends Number></literal>. - Does not match gets of fields of type <literal>List<Number></literal> or - <literal>List<Double></literal> as these are distinct types. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>get(List<Number+> *)</term> - <listitem> - <para>matches any get of a field of type <literal>List<Number></literal> or - any subclass of <literal>Number</literal>. For example, <literal>List<Number>, - List<Double> List<Float></literal>. - Does not match a get join point for a field of type <literal>List<? extends Number></literal> - as this is a distinct type. + <para>From the signature of <literal>foo</literal> all we know is that the runtime argument will be an instance of + <literal>Object</literal>.Compiling this program gives the unchecked argument warning: + "unchecked match of List<? extends Number> with List when argument is + an instance of List at join point method-execution(void C.foo(Object)) [Xlint:uncheckedArgument]". + The advice will not execute at the call join point for <literal>c.foo("hi")</literal> since <literal>String</literal> + is not an instance of <literal>List</literal>. The advice <emphasis>will</emphasis> execute at the call join points + for <literal>c.foo(ls)</literal> and <literal>c.foo(ld)</literal> since in both cases the argument is an instance of + <literal>List</literal>. </para> - </listitem> - </varlistentry> + + <para>Combine a wildcard argument type with a signature pattern to avoid unchecked argument matches. In the example + below we use the signature pattern <literal>List<Number+></literal> to match a call to any method taking + a <literal>List<Number>, List<Double>, List<Float></literal> and so on. In addition the + signature pattern <literal>List<? extends Number+></literal> can be used to match a call to a method + declared to take a <literal>List<? extends Number></literal>, <literal>List<? extends Double></literal> + and so on. Taken together, these restrict matching to only + those join points at which the argument is guaranteed to be an instance of <literal>List<? extends Number></literal>.</para> + + + <programlisting><![CDATA[ + aspect A { + before(List<? extends Number> aListOfSomeNumberType) + : (call(* foo(List<Number+>)) || call(* foo(List<? extends Number+>))) + && args(aListOfSomeNumberType) { + // process list... + } + } + ]]></programlisting> + + </sect3> - <varlistentry> - <term>call(* List<?>.*(..))</term> - <listitem> - <para>results in a compilation error, "declaring type cannot be parameterized (List<?>)". - </para> - </listitem> - </varlistentry> + <sect3> + <title>Binding return values in after returning advice</title> + + <para> + After returning advice can be used to bind the return value from a matched join point. AspectJ 5 supports the use of + a parameterized type in the returning clause, with matching following the same rules as described for args. For + example, the following aspect matches the execution of any method returning a <literal>List</literal>, and makes + the returned list available to the body of the advice. + </para> + <programlisting><![CDATA[ + public aspect A { - </variablelist> - </sect3> + pointcut executionOfAnyMethodReturningAList() : execution(List *(..)); + + after() returning(List<?> listOfSomeType) : executionOfAnyMethodReturningAList() { + for (Object element : listOfSomeType) { + // process element... + } + } - <sect3> - <title>Handler</title> - - <para> - The Java Language Specification states that a generic class may not be a direct or indirect - subclass of <literal>Throwable</literal>. Therefore it is a compilation error to use a generic - or parameterized type pattern in a <literal>handler</literal> pointcut expression. - </para> - - </sect3> - - <sect3> - <title>Runtime type matching: this, target and args</title> - - <para> - Java 5 generics are implemented using a technique known an <emphasis>erasure</emphasis>. - In particular, what gets "erased" is the ability to find out the parameterized runtime type - of an instance of a generic type. You can ask if something is an <literal>instanceof List</literal>, - but not if something is an <literal>instanceof List<String></literal> - </para> - - <para> - The <literal>this, target</literal> and <literal>args</literal> pointcut designators all match - based on the runtime type of the appropriate object (this, target, or argument) at a join point. - To match any parameterization of a generic type, simply use the raw type (type variables are - not permitted with these designators). For example: - </para> - - <variablelist> - - <varlistentry> - <term>target(List)</term> - <listitem> - <para>matches any call to an instance of <literal>List</literal> (including - <literal>List<String>, List<Number></literal>, and so on. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>args (List)</term> - <listitem> - <para>matches any join point with a single argument that is an instance of - <literal>List</literal>. - </para> - </listitem> - </varlistentry> - - </variablelist> - - <para> - 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: - <literal>args(List<String>)</literal>. - </para> - - <para> - 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 <literal>this, target</literal> and <literal>args</literal> is as follows. - </para> - - <para>For a parameterized type specified in an <literal>args</literal> pointcut expression:</para> - - <itemizedlist> - <listitem>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 <literal>void foo(Set<BankAccount>)</literal> - and the pointcut expression - <literal>args(Set<BankAccount>)</literal> then any call or execution join point - for the method will be matched.</listitem> - <listitem>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 <literal>args(List<String>)</literal>will never match a call or - execution join point for a method taking a single argument of type <literal>List<Number></literal> (it is not possible for - a type to implement two different parameterizations of the same interface).</listitem> - <listitem>If an object <emphasis>might</emphasis> 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 method that takes a single argument of type - <literal>List<? extends Number></literal> and - pointcut expression <literal>args(List<Double>)</literal> 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 <literal>@SuppressAjWarnings</literal> - or <literal>@SuppressAjWarnings("unchecked")</literal>.</listitem> - </itemizedlist> - - <para> - When using a parameterized type with the - <literal>this</literal> pointcut designator then a joinpoint is - matched if and only if at least one of the following conditions hold: - </para> - - <itemizedlist> - <listitem> - the runtime type of the <literal>this</literal> object extends or - implements the parameterized type. For example, an object with runtime type <literal>Foo</literal>, - defined as - <literal>class Foo implements List<String></literal>, will match - <literal>this(List<String>)</literal>.</listitem> - <listitem> - The parameterized "this" type is given using a generics wildcard in the pointcut - expression, and the bounds of - the generic runtime type of <literal>this</literal> are such that all valid parameterizations - are matched by the wildcard. For example, the pointcut expression - <literal>this(List<? extends Number>)</literal> will match a <literal>this</literal> - object of type <literal>class Foo<N extends Number> implements List<N></literal>. - If <emphasis>some</emphasis> parameterization may be matched by the wildcard (for - example an object of type <literal>class Foo<N> implements List<N></literal>) the pointcut - will match but with an unchecked warning. - </listitem> - </itemizedlist> - - <para> - Using a parameterized type with the <literal>target</literal> 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 <literal>args</literal>. 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 <emphasis>can</emphasis> - 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 <literal>ajc</literal>. - </para> - - <para> - You've already seen some examples of using the generic wildcard <literal>?</literal> - in parameterized type patterns. Since <literal>this, target</literal> and - <literal>args</literal> match using an instance of test, the generic wildcard can be useful in - 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 attempt to add an element into the list <literal>aList</literal>. - </para> - - <programlisting><![CDATA[ - before(List<? extends Number> aList) : - execution(* org.xyz.Foo.*(..)) && args(aList) { - aList.add(5.0d); // Compilation error on this line - } + } + ]]></programlisting> + + <!-- see java5/generics/ajdk/AfterReturningExamples.aj --> + <para>The pointcut uses the raw type pattern <literal>List</literal>, and hence it + matches methods returning any kind of list (<literal>List<String>, List<Double></literal>, + and so on. We've chosen to bind the returned list as the parameterized type + <literal>List<?></literal> in the advice since Java's type checking will now ensure + that we only perform safe operations on the list.</para> + + <para>Given the class</para> + + <programlisting><![CDATA[ + public class C { + + public List<String> foo(List<String> listOfStrings) {...} + + public List<Double> bar(List<Double> listOfDoubles) {...} + + public List<? extends Number> goo(List<? extends Number> listOfSomeNumberType) {...} + + } ]]></programlisting> - </sect3> - - <sect3> - <title>Declaring pointcuts in generic classes</title> + <para>The advice in the aspect below will run after the execution of <literal>bar</literal> + and bind the return value. It will also run after the execution of <literal>goo</literal> and + bind the return value, but gives an <literal>uncheckedArgument</literal> warning during + compilation. It does <emphasis>not</emphasis> run after the execution of <literal>foo</literal>. + </para> - <para> - AspectJ permits pointcuts to be declared in classes as well as aspects. A pointcut defined - inside a generic class may not use the type variables of the class in the pointcut expression - (just as static members of a generic class may not use type variables). - For example: - </para> + <programlisting><![CDATA[ + public aspect Returning { + + after() returning(List<Double> listOfDoubles) : execution(* C.*(..)) { + for(Double d : listOfDoubles) { + // process double... + } + } + + } + ]]></programlisting> + + <para>As with <literal>args</literal> you can guarantee that after returning advice only + executes on lists <emphasis>statically determinable</emphasis> to be of the right + type by specifying a return type pattern in the associated pointcut. The + <literal>@SuppressAjWarnings</literal> annotation can also be used if desired.</para> + + </sect3> - <programlisting><![CDATA[ - public class Foo<T extends Number> { - - ... - - // Not allowed - uses T in the pointcut expression - public pointcut fooOperationCall(T t) : - call(* Foo<T>.*(T)) && args(t); + <sect3> + <title>Declaring pointcuts inside generic types</title> + <para><emphasis>This language feature will not be supported until AspectJ 5 M4.</emphasis></para> - // permitted, but recommended to use an alternate variable name in the local - // type variable declaration - e.g. execution<S>(...) - public pointcut fooExecution(Number n) : - execution<T>(* Foo<T>.*(T)) && args(n); - } - ]]></programlisting> - - </sect3> + <para>Pointcuts can be declared in both classes and aspects. A pointcut declared in a generic + type may use the type variables of the type in which it is declared. All references to + a pointcut declared in a generic type from outside of that type must be via a parameterized type reference, + and not a raw type reference.</para> + <para>Consider the generic type <literal>Generic</literal> with a pointcut <literal>foo</literal>: + </para> + + <programlisting><![CDATA[ + public class Generic<T> { + + /** + * matches the execution of any implementation of a method defined for T + */ + public pointcut foo() : execution(* T.*(..)); + + } + ]]></programlisting> + + <!-- see java5/generics/ajdk/PointcutInGenericClassExample.aj --> + + <para>Such a pointcut must be refered to using a parameterized reference as shown + below.</para> + + <programlisting><![CDATA[ + public aspect A { + + // runs before the execution of any implementation of a method defined for MyClass + before() : Generic<MyClass>.foo() { + // ... + } + + // runs before the execution of any implementation of a method defined for YourClass + before() : Generic<YourClass>.foo() { + // ... + } + + // results in a compilation error - raw type reference + before() : Generic.foo() { } + + } + ]]></programlisting> + + </sect3> + </sect2> <sect2> <title>Inter-type Declarations</title> <para> - AspectJ 5 allows type parameters to be used in inter-type declarations - either for declaring generic - methods and constructors, or for declaring members on generic types. The syntax for declaring generic - methods and constructors follows the regular AspectJ convention of simply qualifying the member name with - the target type. + AspectJ 5 supports the inter-type declaration of generic methods, and of members on + generic types. For generic methods, the syntax is exactly as for a regular method + declaration, with the addition of the target type specification: </para> <variablelist> @@ -1273,18 +956,21 @@ <varlistentry> <term>String Foo.getName() {...}</term> <listitem> - <para>Declares a <literal>getName</literal> method on behalf of the raw type <literal>Foo</literal>. It is + <para>Declares a <literal>getName</literal> method on behalf of the type <literal>Foo</literal>. It is not possible to refer to the type parameters of Foo in such a declaration. </para> </listitem> </varlistentry> <varlistentry> - <term>R Foo<Q, R>.getMagnitude() {...}</term> + <term>public R Foo<Q, R>.getMagnitude() {...}</term> <listitem> <para>Declares a method <literal>getMagnitude</literal> on the generic class <literal>Foo</literal>. The method returns an instance of the type substituted for the second type parameter in an invocation - of <literal>Foo</literal>. + of <literal>Foo</literal> If <literal>Foo</literal> is declared as + <literal>Foo<T,N extends Number> {...}</literal> then this inter-type declaration is + equivalent to the declaration of a method <literal>public N getMagnitude()</literal> + within the body of <literal>Foo</literal>. </para> </listitem> </varlistentry> @@ -1304,58 +990,18 @@ <para>A parameterized type may not be the target of an inter-type declaration. This is because there is only one type (the generic type) regardless of how many different invocations (parameterizations) of that generic type are made in a program. Therefore it does not make sense to try and declare a member - on behalf of (say) <literal>Foo<String></literal>, you can only declare members on the generic - type <literal>Foo<T></literal>. - </para> - - <para> - If an inter-type member is declared inside a generic aspect, then the type parameter names from the - aspect declaration may be used in the signature specification of the inter-type declaration, but - <emphasis>not</emphasis> as type parameter names for a generic target type. In other words the example - that follows is legal: + on behalf of (say) <literal>Bar<String></literal>, you can only declare members on the generic + type <literal>Bar<T></literal>. </para> - - <programlisting><![CDATA[ - public abstract aspect A<T> { - - private T Foo.data; - - public T Foo.getData(T defaultValue) { - return (this.data != null ? data : defaultValue); - } - - } - ]]></programlisting> - <para> - Whereas the following example is not allowed and will report an error that a parameterized type may not be the - target of an inter-type declaration (since when the type parameter <literal>T</literal> in the aspect is subsituted with - say, <literal>String</literal>, then the target of the inter-type declaration becomes <literal>Goo<String></literal>). - </para> - - <programlisting><![CDATA[ - public abstract aspect A<T> { - - private T Goo<T>.data; - - public T Goo<T>.getData(T defaultValue) { - return (this.data != null ? data : defaultValue); - } - - } - ]]></programlisting> - </sect2> - + <sect2> <title>Declare Parents</title> <para>Both generic and parameterized types can be used as the parent type in a <literal>declare parents</literal> 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 <literal>declare parents</literal> statement: - a type variable list follows the <literal>parents</literal> keyword in these cases to declare the - type variables in scope. - Some examples follow:</para> + rules). Generic types may also be used as the target type of a <literal>declare parents</literal> statement.</para> <variablelist> @@ -1370,24 +1016,6 @@ </listitem> </varlistentry> - <varlistentry> - <term>declare parents <T>: org.xyz..*<T> extends Base<T></term> - <listitem> - <para>All generic types declared in a package beginning with <literal>org.xyz</literal> and with a - single unbounded type parameter, extend the generic type <literal>Base<T></literal>. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term>declare parents <T>: org.xyz..*<T> extends Base<S></term> - <listitem> - <para>Results in a compilation error (unless <literal>S</literal> is a type) since <literal>S</literal> is - not bound in the type pattern. - </para> - </listitem> - </varlistentry> - </variablelist> </sect2> @@ -1399,7 +1027,9 @@ </sect2> <sect2> - <title>Parameterized Aspects</title> + <title>Generic Aspects</title> + + <para><emphasis>This feature will not be fully implemented until AspectJ5 M4.</emphasis></para> <para> AspectJ 5 allows an <emphasis>abstract</emphasis> aspect to be declared as a generic type. Any concrete @@ -1455,83 +1085,105 @@ </varlistentry> </variablelist> - - <para>An exception to the rule that concrete aspects may not be generic is a pertypewithin aspect, which - may be declared with a single unbounded type parameter. This is discussed in the chapter on <xref - linkend="pertypewithin" />.</para> <para>The type parameter variables from a generic aspect declaration may be used in place of a type within any - member of the aspect. For example, we can declare a <literal>ParentChildRelationship</literal> aspect to + member of the aspect, <emphasis>except for within inter-type declarations</emphasis>. + For example, we can declare a <literal>ParentChildRelationship</literal> aspect to manage the bi-directional relationship between parent and child nodes as follows: </para> <programlisting><![CDATA[ - public abstract aspect ParentChildRelationship<P,C> { - - /** - * Parents contain a list of children - */ - private List<C> P.children; - - /** - * Each child has a parent - */ - private P C.parent; - - /** - * Parents provide access to their children - */ - public List<C> P.getChildren() { - return Collections.unmodifiableList(children); - } - - /** - * A child provides access to its parent - */ - public P C.getParent() { - return parent; + /** + * a generic aspect, we've used descriptive role names for the type variables + * (Parent and Child) but you could use anything of course + */ + public abstract aspect ParentChildRelationship<Parent,Child> { + + /** generic interface implemented by parents */ + interface ParentHasChildren<C>{ + List<C> getChildren(); + void addChild(C child); + void removeChild(C child); + } + + /** generic interface implemented by children */ + interface ChildHasParent<P>{ + P getParent(); + void setParent(P parent); + } + + /** ensure the parent type implements ParentHasChildren<child type> */ + declare parents: Parent implements ParentHasChildren<Child>; + + /** ensure the child type implements ChildHasParent<parent type> */ + declare parents: Child implements ChildHasParent<Parent>; + + // Inter-type declarations made on the *generic* interface types to provide + // default implementations. + + /** list of children maintained by parent */ + private List<C> ParentHasChildren<C>.children; + + /** reference to parent maintained by child */ + private P ChildHasParent<P>.parent; + + /** Default implementation of getChildren for the generic type ParentHasChildren */ + public List<C> ParentHasChildren<C>.getChildren() { + return Collections.unmodifiableList(children); + } + + /** Default implementation of getParent for the generic type ChildHasParent */ + public P ChildHasParent<P>.getParent() { + return parent; + } + + /** + * Default implementation of addChild, ensures that parent of child is + * also updated. + */ + public void ParentHasChildren<C>.addChild(C child) { + if (child.parent != null) { + child.parent.removeChild(child); + } + children.add(child); + child.parent = this; + } + + /** + * Default implementation of removeChild, ensures that parent of + * child is also updated. + */ + public void ParentHasChildren<C>.removeChild(C child) { + if (children.remove(child)) { + child.parent = null; } + } + + /** + * Default implementation of setParent for the generic type ChildHasParent. + * Ensures that this child is added to the children of the parent too. + */ + public void ChildHasParent<P>.setParent(P parent) { + parent.addChild(this); + } + + /** + * Matches at an addChild join point for the parent type P and child type C + */ + public pointcut addingChild(Parent p, Child c) : + execution(* Parent.addChild(Child)) && this(p) && args(c); - /** - * ensure bi-directional navigation on adding a child - */ - public void P.addChild(C child) { - if (child.parent != null) { - child.parent.removeChild(child); - } - children.add(child); - child.parent = this; - } - - /** - * ensure bi-directional navigation on removing a child - */ - public void P.removeChild(C child) { - if (children.remove(child)) { - child.parent = null; - } - } + /** + * Matches at a removeChild join point for the parent type P and child type C + */ + public pointcut removingChild(Parent p, Child c) : + execution(* Parent.removeChild(C)) && this(p) && args(c); - /** - * ensure bi-directional navigation on setting parent - */ - public void C.setParent(P parent) { - parent.addChild(this); - } - - public pointcut addingChild(P p, C c) : - execution(* P.addChild(C)) && this(p) && args(c); - - public pointcut removingChild(P p, C c) : - execution(* P.removeChild(C)) && this(p) && args(c); - } + } + ]]></programlisting> - <para> - Note in the above example how the type parameters <literal>P</literal> and <literal>C</literal> can be - used in inter-type declarations, pointcut expressions, and any other member of the aspect type. - </para> - + <para> The example aspect captures the protocol for managing a bi-directional parent-child relationship between any two types playing the role of parent and child. In a compiler implementation managing an abstract syntax @@ -1563,80 +1215,87 @@ </simplelist> <para> - In a system managing files and folders, we could declare the concrete aspect: + In a system managing orders, we could declare the concrete aspect: </para> <programlisting><![CDATA[ - public aspect FilesInFolders extends ParentChildRelationship<Folder,File> { + public aspect OrderItemsInOrders extends ParentChildRelationship<Order,OrderItem> { } ]]></programlisting> <para> - As a result of this declaration, <literal>Folder</literal> gains members: + As a result of this declaration, <literal>Order</literal> gains members: </para> <simplelist> - <member><literal>List<File> children</literal></member> - <member><literal>List<File> getChildren()</literal></member> - <member><literal>void addChild(File child)</literal></member> - <member><literal>void removeChild(File child)</literal></member> + <member><literal>List<OrderItem> children</literal></member> + <member><literal>List<OrderItem> getChildren()</literal></member> + <member><literal>void addChild(OrderItem child)</literal></member> + <member><literal>void removeChild(OrderItem child)</literal></member> </simplelist> - <para>and <literal>File</literal> gains members:</para> + <para>and <literal>OrderItem</literal> gains members:</para> <simplelist> - <member><literal>Folder parent</literal></member> - <member><literal>Folder getParent()</literal></member> - <member><literal>void setParent(Folder parent)</literal></member> + <member><literal>Order parent</literal></member> + <member><literal>Order getParent()</literal></member> + <member><literal>void setParent(Order parent)</literal></member> </simplelist> - <para>When used in this way, the type parameters in a generic abstract aspect declare - <emphasis>roles</emphasis>, and the parameterization of the abstract aspect in the <literal>extends</literal> - clause binds types to those roles. This is a case where you may consider departing from the standard practice - of using a single letter to represent a type parameter, and instead use a role name. It makes no difference - to the compiler which option you choose of course.</para> - - <programlisting><![CDATA[ - public abstract aspect ParentChildRelationship<Parent,Child> { - - /** - * Parents contain a list of children - */ - private List<Child> Parent.children; - - /** - * Each child has a parent - */ - private Parent Child.parent; - - /** - * Parents provide access to their children - */ - public List<Children> Parent.getChildren() { - return Collections.unmodifiableList(children); - } - - /** - * A child provides access to its parent - */ - public Parent Children.getParent() { - return parent; - } - - /** - * ensure bi-directional navigation on adding a child - */ - public void Parent.addChild(Child child) { - if (child.parent != null) { - child.parent.removeChild(child); - } - children.add(child); - child.parent = this; - } - ... - ]]></programlisting> + <para>A second example of an abstract aspect, this time for handling exceptions in a uniform + manner, is shown below:</para> + + <programlisting><![CDATA[ + abstract aspect ExceptionHandling<T extends Throwable> { + + /** + * method to be implemented by sub-aspects to handle thrown exceptions + */ + protected abstract void onException(T anException); + + /** + * to be defined by sub-aspects to specify the scope of exception handling + */ + protected abstract pointcut inExceptionHandlingScope(); + + /** + * soften T within the scope of the aspect + */ + declare soft: T : inExceptionHandlingScope(); + + /** + * bind an exception thrown in scope and pass it to the handler + */ + after() throwing (T anException) : inExceptionHandlingScope() { + onException(anException); + } + + } + ]]></programlisting> + + <para>Notice how the type variable <literal>T extends Throwable</literal> allows the + components of the aspect to be designed to work together in a type-safe manner. The + following concrete sub-aspect shows how the abstract aspect might be extended to + handle <literal>IOExceptions</literal>.</para> + + <programlisting><![CDATA[ + public aspect IOExceptionHandling extends ExceptionHandling<IOException>{ + + protected pointcut inExceptionHandlingScope() : + call(* doIO*(..)) && within(org.xyz..*); + + /** + * called whenever an IOException is thrown in scope. + */ + protected void onException(IOException ex) { + System.err.println("handled exception: " + ex.getMessage()); + throw new MyDomainException(ex); + } + } + ]]></programlisting> + </sect2> </sect1> |