1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
<chapter id="varargs" xreflabel="Varargs">
<title>Varargs</title>
<sect1 id="varargs-inJava5" xreflabel="Varargs in Java 5">
<title>Variable-length Argument Lists in Java 5</title>
<para>
Java 5 (and hence AspectJ 5) allows you to specify methods that take a
variable number of arguments of a specified type. This is achieved using
an ellipsis (...) in the method signature as shown:
</para>
<programlisting><![CDATA[
public void foo(int i, String... strings) {
}
]]></programlisting>
<para>
A method or constructor may take at most one variable length argument, and
this must always be the last declared argument in the signature.
</para>
<sect2 id="calling-methods-and-constructors-with-variable-length-arguments" xreflabel="calling-methods-and-constructors-with-variable-length-arguments">
<title>Calling Methods and Constructors with variable-length arguments</title>
<para>
A <emphasis>varargs</emphasis> method may be called with zero or more arguments
in the variable argument position. For example, given the definition of
<literal>foo</literal> above, the following calls are all legal:
</para>
<programlisting><![CDATA[
foo(5);
foo(5,"One String");
foo(7,"One String","Two Strings");
foo(3,"One String","Two Strings","Three Strings");
]]></programlisting>
<para>A <emphasis>varargs</emphasis> parameter is treated as an array within the
defining member. So in the body of <literal>foo</literal> we could write for example:
</para>
<programlisting><![CDATA[
public void foo(int i, String... strings) {
String[] someStrings = strings;
// rest of method body
}
]]></programlisting>
<para>One consequence of this treatment of a varargs parameter as an array
is that you can also call a varargs method with an array:</para>
<programlisting><![CDATA[
foo(7,new String[] {"One String","Two Strings"});
]]></programlisting>
</sect2>
</sect1>
<sect1 id="varargs-in-pcds">
<title>Using Variable-length arguments in advice and pointcut expressions</title>
<para>AspectJ 5 allows variable-length arguments to be used for methods declared within
aspects, and for inter-type declared methods and constructors, in accordance with the rules
outlined in the previous section.</para>
<para>
AspectJ 5 also allows variable length arguments to be matched by pointcut expressions and
bound as formals in advice.
</para>
<sect2 id="matching-signatures-based-on-variable-length-argument-types" xreflabel="matching-signatures-based-on-variable-length-argument-types">
<title>Matching signatures based on variable length argument types</title>
<para>
Recall from the definition of signature patterns given in the chapter on
annotations (<xref linkend="signaturePatterns"/>), that <literal>MethodPattern</literal>
and <literal>ConstructorPattern</literal> are extended to allow a <literal>varargs</literal>
pattern in the last argument position of a method or constructor signature.
</para>
<programlisting><![CDATA[
FormalsPattern := '..' (',' FormalsPatternAfterDotDot)? |
OptionalParensTypePattern (',' FormalsPattern)* |
TypePattern '...'
FormalsPatternAfterDotDot :=
OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
TypePattern '...'
]]></programlisting>
<para>
Method and constructor patterns are used in the <literal>call</literal>,
<literal>execution</literal>, <literal>initialization</literal>,
<literal>preinitialization</literal>, and <literal>withincode</literal>
pointcut designators. Some examples of usage follow:
</para>
<variablelist>
<varlistentry>
<term>call(* org.xyz.*.*(int, String...))</term>
<listitem>
<para>
Matches a call join point for a call to a method defined in the
<literal>org.xyz</literal> package, taking an <literal>int</literal>
and a <literal>String vararg</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>execution(* org.xyz.*.*(Integer...))</term>
<listitem>
<para>
Matches an execution join point for the execution of a method defined in the
<literal>org.xyz</literal> package, taking an <literal>Integer vararg</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>initialization(org.xyz.*.new((Foo || Goo)...))</term>
<listitem>
<para>
Matches the initialization join point for the construction of an
object in the <literal>org.xyz</literal> package via a constructor
taking either a variable number of <literal>Foo</literal> parameters or
a variable number of <literal>Goo</literal> parameters. (This example
illustrating the use of a type pattern with ...).
</para>
</listitem>
</varlistentry>
</variablelist>
<para>A variable argument parameter and an array parameter are treated as distinct
signature elements, so given the method definitions:
</para>
<programlisting><![CDATA[
void foo(String...);
void bar(String[]);
]]></programlisting>
<para>
The pointcut <literal>execution(* *.*(String...))</literal> matches the execution join point
for <literal>foo</literal>, but not <literal>bar</literal>. The pointcut
<literal>execution(* *.*(String[]))</literal> matches the execution join point
for <literal>bar</literal> but not <literal>foo</literal>.
</para>
</sect2>
<sect2 id="exposing-variable-length-arguments-as-context-in-pointcuts-and-advice" xreflabel="exposing-variable-length-arguments-as-context-in-pointcuts-and-advice">
<title>Exposing variable-length arguments as context in pointcuts and advice</title>
<para>
When a varargs parameter is used within the body of a method, it has
an array type, as discussed in the introduction to this section. We follow the
same convention when binding a varargs parameter via the <literal>args</literal>
pointcut designator. Given a method
</para>
<programlisting><![CDATA[
public void foo(int i, String... strings) {
}
]]></programlisting>
<para>
The call or execution join points for <literal>foo</literal> will be matched
by the pointcut <literal>args(int,String[])</literal>. It is not permitted
to use the varargs syntax within an args pointcut designator - so you
<emphasis>cannot</emphasis> write <literal>args(int,String...)</literal>.
</para>
<para>
Binding of a varargs parameter in an advice statement is straightforward:
</para>
<programlisting><![CDATA[
before(int i, String[] ss) : call(* foo(int,String...)) && args(i,ss) {
// varargs String... argument is accessible in advice body through ss
// ...
}
]]></programlisting>
<para>Since you cannot use the varargs syntax in the <literal>args</literal>
pointcut designator, you also cannot use the varargs syntax to declare
advice parameters.</para>
<para>Note: the proposal in this section does not allow you to
distinguish between a join point with a signature (int, String...)
and a join point with a signature (int, String[]) based
<emphasis>solely</emphasis> on the use of the <literal>args</literal>
pointcut designator. If this distinction is required, <literal>args</literal>
can always be coupled with <literal>call</literal> or
<literal>execution</literal>.</para>
</sect2>
</sect1>
</chapter>
|