javaaspectjpointcuts

AspectJ parameterized types not support for target pointcuts


I would like to create a class that pointcuts to methods within the list interface. This seems to work until I add the target(list) to my advice. I would like to view the elements of a list before and after adding to (for example) see what has changed but I cannot seem to pass my list in as I would an object. Here is what I have so far, this does not run with the target(list) but does run without it :

pointcut addPointCut() : call(boolean List.add(..));

before(List<Integer> list) : addPointCut() && target(list) {
    System.out.println("testing");
    for(Object i : list) {
        System.out.println(i);
    }
}

Solution

  • Nándor's answer is correct insofar as he describes the compiler error you see. I want to go a little further and also explain why you get this error:

    First of all, the problem is not directly related to AspectJ but rather to how Java implements generics. Please make yourself familiar with a phenomenon called type erasure before you continue to read.

    Well, because type erasure is a JVM reality and because target() and this() are resolved during runtime rather than compile time - you can indirectly conclude this from the fact that both getTarget() and getThis() are methods of JoinPoint rather than JoinPoint.StaticPart - what you want to do cannot work and consequently leads to an AspectJ compiler error. The only thing you can do is use instanceof in order to dynamically determine what gets added to the target list. The most elegant way to do this is an if() pointcut expression.

    Here is some sample code:

    Driver application:

    So as to make things a little more interesting we use two types of lists and also two types of add(..) calls. The goal should be to only intercept integers to be added to the corresponding list, no matter which signature the add(..) method has.

    package de.scrum_master.app;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Application {
        public static void main(String[] args) {
            List<Integer> integers = new ArrayList<>();
            integers.add(11);
            integers.add(0, 22);
            integers.add(33);
    
            List<String> strings = new ArrayList<>();
            strings.add("foo");
            strings.add(0, "bar");
            strings.add("zot");
        }
    }
    

    Aspect:

    package de.scrum_master.aspect;
    
    import java.util.List;
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    public aspect GenericsAspect {
        pointcut addPointCut(List list, Object newElement) :
            !within(GenericsAspect) &&            // avoid stack overflow due to recursion
            call(* List.add(..)) &&               // intercept all calls to List.add
            args(.., newElement) &&               // capture last method parameter
            if(newElement instanceof Integer) &&  // only capture added int/Integer elements
            target(list);                         // target is a List
    
        before(List list, Object newElement) :
            addPointCut(list, newElement)
        {
            System.out.println(thisJoinPoint + " -> new element = " + newElement);
            for(Object i : list)
                System.out.println("  " + i);
    
            // Type erasure in action:
            // During runtime there is no such thing as List<Integer>, only a raw List.
            // Thus, we can easily add a String to a list declared as List<Integer>.
            list.add("#" + newElement + "#");
        }
    }
    

    Console log:

    call(boolean java.util.List.add(Object)) -> new element = 11
    call(void java.util.List.add(int, Object)) -> new element = 22
      #11#
      11
    call(boolean java.util.List.add(Object)) -> new element = 33
      22
      #11#
      11
      #22#
    

    Any further questions?