Search code examples
javagenericsarchunit

Archunit matching on a constructor for a generic type


I want to make an ArchUnit rule that picks up calls to Spring's ResponseEntity that look like this:

new ResponseEntity<>("No record", HttpStatus.NOT_FOUND);

I tried a rule like this:

    ArchRule rule = noClasses().should().
        callConstructor(ResponseEntity.class, String.class, HttpStatus.class).
        because("foo");

but it doesn't seem to be match. My guess at the moment is that this is because of the generic type, as I changing the rule to target a non-generic type and that seemed to work.

I have a feeling that using callConstructorWhere() might be part of the solution but I'm struggling to find an example of how to use that and can't seem to find the right part of the documentation. I'm using ArchUnit 0.23.1.


Solution

  • Your guess is correct: The generic constructor ResponseEntity(T, HttpStatus) has erased parameter types (Object, HttpStatus),¹ and callConstructor looks for these raw types.

    Unfortunately, even if you use callConstructorWhere with a custom predicate for the JavaConstructorCall, you can only get access to the target owner as (raw) JavaClass, but not as JavaParameterizedType that could hold the specific type argument T = String in your case.

    So I fear that your issue cannot be solved with the current version of ArchUnit.


    ¹ With your spring-web.jar at hand, you can investigate this using

    jar xf spring-web-*.jar
    
    javap -v org/springframework/http/ResponseEntity.class \
    | grep -A1 'ResponseEntity(T, org.springframework.http.HttpStatus)'
    

    For spring-web-5.3.22.jar, I get:

      public org.springframework.http.ResponseEntity(T, org.springframework.http.HttpStatus);
        descriptor: (Ljava/lang/Object;Lorg/springframework/http/HttpStatus;)V