Using Variable-length arguments in advice and pointcut expressions

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.

AspectJ 5 also allows variable length arguments to be matched by pointcut expressions and bound as formals in advice.

Matching signatures based on variable length argument types

Recall from the definition of signature patterns given in the chapter on annotations (Signature Patterns), that MethodPattern and ConstructorPattern are extended to allow a varargs pattern in the last argument position of a method or constructor signature.

  	
 		FormalsPattern := '..' (',' FormalsPatternAfterDotDot)? |
		                  OptionalParensTypePattern (',' FormalsPattern)* |
		                  TypePattern '...'
		                  
		FormalsPatternAfterDotDot := 
		        OptionalParensTypePattern (',' FormalsPatternAfterDotDot)* |
		        TypePattern '...'

    	

Method and constructor patterns are used in the call, execution, initialization, preinitialization, and withincode pointcut designators. Some examples of usage follow:

call(* org.xyz.*.*(int, String...))

Matches a call join point for a call to a method defined in the org.xyz package, taking an int and a String vararg.

execution(* org.xyz.*.*(Integer...))

Matches an execution join point for the execution of a method defined in the org.xyz package, taking an Integer vararg.

initialization(org.xyz.*.new((Foo || Goo)...))

Matches the initialization join point for the construction of an object in the org.xyz package via a constructor taking either a variable number of Foo parameters or a variable number of Goo parameters. (This example illustrating the use of a type pattern with ...).

A variable argument parameter and an array parameter are treated as distinct signature elements, so given the method definitions:

    	void foo(String...);
    	void bar(String[]);
    	

The pointcut execution(* *.*(String...)) matches the execution join point for foo, but not bar. The pointcut execution(* *.*(String[])) matches the execution join point for bar but not foo.

Exposing variable-length arguments as context in pointcuts and advice

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 args pointcut designator. Given a method

		public void foo(int i, String... strings) { 
		}
		

The call or execution join points for foo will be matched by the pointcut args(int,String[]). It is not permitted to use the varargs syntax within an args pointcut designator - so you cannot write args(int,String...).

Binding of a varargs parameter in an advice statement is straightforward:

		before(int i, String[] ss) : call(* foo(int,String...)) && args(i,ss) {
		  // varargs String... argument is accessible in advice body through ss
		  // ...
		}
		

Since you cannot use the varargs syntax in the args pointcut designator, you also cannot use the varargs syntax to declare advice parameters.

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 solely on the use of the args pointcut designator. If this distinction is required, args can always be coupled with call or execution.