Search code examples
javaannotation-processing

Java annotation processor comparing method return `TypeMirror` with known class


My Java annotation processor is generating an implementation of an interface, which has a MyInterface.foo() method returning CompletableFuture<String>. In my processor the ExecutableElement for that method indicates a getReturnType() TypeMirror with a toString() value of java.util.concurrent.CompletableFuture<java.lang.String>, so I know I have the right TypeMirror for the return type.

Now I want to test in code whether this TypeMirror instance is indeed a CompletableFuture of some sort. I tried Types.isAssignable(…), Types.isSubtype(…), and Types.isSameType(…), but none seem to work. For example:

TypeElement completableFutureTypeElement = processingEnv.getElementUtils()
    .getTypeElement(CompletableFuture.class.getCanonicalName());
boolean isCompletableFutureReturnType = processingEnv.getTypeUtils()
    .isSubtype(returnTypeMirror, completableFutureTypeElement.asType());

Am I using the wrong method for checking? Or is the problem that the return type is CompletableFuture<String> and I'm comparing against the raw type? In any case, how do I simply check to see that non-parameterized (i.e. raw) class of return type is CompletableFuture? I suppose I could just compare strings, but is there are more type-safe, semantic way?

My related question is how to get a TypeMirror for a known class, as I did above for completableFutureTypeElement and then completableFutureTypeElement.asType(). Is there a more direct way to get a TypeElement and/or TypeMirror for a class such as CompletableFuture.class without doing a lookup on the string form of its name?


Solution

  • It appears that the comparison technique in the question doesn't work because it compares the raw type to the actual parameterized type. If I cast the return TypeMirror to a DeclaredType, get its Element representation, and then convert back to a TypeMirror, then the comparison works—I suppose because I'm in effect applying erasure to the returned type:

    TypeElement completableFutureTypeElement = processingEnv.getElementUtils()
        .getTypeElement(CompletableFuture.class.getCanonicalName());
    boolean isCompletableFutureReturnType = processingEnv.getTypeUtils()
        .isSubtype(((DeclaredType)returnTypeMirror).asElement().asType(),
            completableFutureTypeElement.asType());
    

    That's seems to be a bit of a hack, though. Calling processingEnv.getTypeUtils().erasure(returnTypeMirror) on the return type mirror, which sounds like it would do the same thing, doesn't work, though. Moreover with this approach I can't seem to use isSubtype(…) or isAssignable(…) with Future, even though CompletableFuture implements Future.

    Instead, the more semantic way seems to go in the other direction: start by creating a TypeMirror for a parameterized CompletableFuture<?> against which to compare—as you would do in Java code. You can create a parameterized unbounded wildcard using Types.getDeclaredType(…) from the TypeElement already created; along with Types.getWildcard(…), passing null to indicate no extends or super bounds (i.e. the ? wildcard). Then you use isAssignable(…) to see if the return type is something that can be assigned to the parameterized type you constructed.

    TypeElement completableFutureTypeElement = processingEnv.getElementUtils()
        .getTypeElement(CompletableFuture.class.getCanonicalName());
    DeclaredType completableFutureWildcardTypeElement =
        processingEnv.getTypeUtils().getDeclaredType(completableFutureTypeElement,
            processingEnv.getTypeUtils().getWildcardType(null, null));
    boolean isCompletableFutureReturnType = processingEnv.getTypeUtils()
        .isAssignable(returnTypeMirror, completableFutureWildcardTypeElement);
    

    As a bonus, isAssignable(…) with a return type of CompletableFuture<String> will work against Future<?> as well, so that's a good indication that this second approach of creating parameterized unbounded wildcard types is the more semantic technique.