Search code examples
javainheritancemirrorannotation-processing

Checking for absence of super class in annotation processor


When obtaining a TypeElement in an annotation processor, you can ask for its super class (or more specifically, the TypeMirror of it) using method getSuperClass(). According to the JavaDoc, a type that doesn't explicity extend anything (in other words, Object is the super class) or that is an interface will return a NoType with NONE as TypeKind.

Disregarding the fact that the whole model/mirror API thing seems to go out of its way to confuse you at every opportunity for a moment, how would I reliably check for this? Here's some code extracts:

//Cast is safe since TypeKind is checked before this
final TypeElement el = (TypeElement)annotatedElement;
...
final TypeMirror parent = el.getSuperclass();
//Checking whether "nothing" is extended
if(parent.getKind().equals(TypeKind.NONE) && parent instanceof NoType) {
    ...         
}

Is this the proper manner? It seems rather clunky. Since the equals method of TypeMirror should not be used for semantic equality checks, I'm wondering whether it is safe to use it for TypeKind. My gut feeling says yes since it's an enum, but then I'm having doubts about that instanceof.

Is there a better way of doing this? The whole javax.lang.model package and its children have cross-references all over the shop and it's never really clear to me what the best method for something is. There's useful utility methods for some stuff, and then there are seemingly simple tasks that require dubious acrobatics like the above.


Solution

  • I just did a test and realized I misinterpreted the documentation. It returns a NoType with kind NONE for java.lang.Object and interfaces, not for something implicitly extending Object. Silly me. I won't need the check since an earlier one checks if the annotated element is a class in the strict sense (not an enum or interface) and will return if so. In other words, checking if the canonical name is java.lang.Object or maybe using Types.isSameType should do the trick.

    Sorry folks. I've decided not to delete since others might arrive with the same question. Feel free to vote delete if you believe it's appropriate.

    EDIT: Here's what I eventually went with...

    boolean superTypeValid = true;
    final TypeMirror parent = el.getSuperClass();
    if(!parent.getKind().equals(TypeKind.DECLARED)) {
        messager.printMessage(Kind.ERROR, "May only inherit from a class; not enums, annotations or other declared kinds.", annotatedElement, mirror);
        superTypeValid = false;
    } else {
        final DeclaredType parentType = (DeclaredType)parent;
        final Element parentEl = parentType.asElement();
        if(!parentEl.getKind().equals(ElementKind.CLASS)) {
            messager.printMessage(Kind.ERROR, "May only inherit from a class; not enums, annotations or other declared kinds.", annotatedElement, mirror);
            superTypeValid = false;
        }
    }
    ...
    if(superTypeValid) {
        if(typeUtils.isSameType(parent, elUtils.getTypeElement("java.lang.Object").asType())) {
            messager.printMessage(Kind.ERROR, "Inherit must not be set to true if the class doesn't extend.", annotatedElement, mirror);
            valid = false;
        } else {
            ...
        }
    }
    

    If some of those error messages seem weird, it's because I left some project-specific stuff out.