【问题标题】:Getting the qualified class name of generic type with Java 6 annotation processor使用 Java 6 注解处理器获取泛型类型的限定类名
【发布时间】:2012-03-24 03:19:44
【问题描述】:

我正在使用 JDK 6 的注释处理 API 开发一个小型代码生成器,并且一直试图获取类中字段的实际泛型类型。为了更清楚,假设我有一个这样的课程:

@MyAnnotation
public class User {         
    private String id;
    private String username;
    private String password;
    private Set<Role> roles = new HashSet<Role>();
    private UserProfile profile;
}

这是我的注释处理器类:

@SupportedAnnotationTypes({ "xxx.MyAnnotation" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MongoDocumentAnnotationProcessor extends AbstractProcessor {

    private Types typeUtils = null;
    private Elements elementUtils = null;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        debug("Running " + getClass().getSimpleName());
        if (roundEnv.processingOver() || annotations.size() == 0) {
            return false;
        }
        for (Element element : roundEnv.getRootElements()) {
            if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
                for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
                    String fieldName = variableElement.getSimpleName().toString();
                    Element innerElement = typeUtils.asElement(variableElement.asType());
                    String fieldClass = "";
                    if (innerElement == null) { // Primitive type
                        PrimitiveType primitiveType = (PrimitiveType) variableElement.asType();
                        fieldClass = typeUtils.boxedClass(primitiveType).getQualifiedName().toString();
                    } else {
                        if (innerElement instanceof TypeElement) {
                            TypeElement typeElement = (TypeElement) innerElement;
                            fieldClass = typeElement.getQualifiedName().toString();
                            TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
                            if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
                                TypeVariable typeMirror = (TypeVariable)((DeclaredType)typeElement.asType()).getTypeArguments().get(0);
                                TypeParameterElement typeParameterElement = (TypeParameterElement) typeUtils.asElement(typeMirror);
                                // I am stuck here. I don't know how to get the
                                // full qualified class name of the generic type of
                                // property 'roles' when the code processes the User
                                // class as above. What I want to retrieve is the
                                // 'my.package.Role' value
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean isAnnotated(Element element) {
        List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
        if (annotationMirrors == null || annotationMirrors.size() == 0) return false;
        for (AnnotationMirror annotationMirror : annotationMirrors) {
            String qualifiedName = ((TypeElement)annotationMirror.getAnnotationType().asElement()).getQualifiedName().toString();
            if ("xxx.MyAnnotation".equals(qualifiedName)) return true;
        }
        return false;
    }
}

任何提示将不胜感激!

【问题讨论】:

  • @Mike Samuel:我认为注释处理发生在编译过程之前,所以类型擦除还没有发生。此外,我使用的是 Java Annotation Processor API 而不是 Reflection API,所以我认为这是可能的,如果我错了,请纠正我

标签: java generics annotations annotation-processing


【解决方案1】:

复制粘贴我的original answer:

这似乎是一个常见问题,所以对于那些来自 Google 的人来说:有希望。

Dagger DI 项目在 Apache 2.0 许可下获得许可,并包含一些用于在注释处理器中处理类型的实用方法。

特别是,Util 类可以在 GitHub (Util.java) 上完整查看,并定义了一个方法public static String typeToString(TypeMirror type)。它使用 TypeVisitor 和一些递归调用来构建类型的字符串表示。这是一个sn-p供参考:

public static void typeToString(final TypeMirror type, final StringBuilder result, final char innerClassSeparator)
{
    type.accept(new SimpleTypeVisitor6<Void, Void>()
    {
        @Override
        public Void visitDeclared(DeclaredType declaredType, Void v)
        {
            TypeElement typeElement = (TypeElement) declaredType.asElement();

            rawTypeToString(result, typeElement, innerClassSeparator);

            List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
            if (!typeArguments.isEmpty())
            {
                result.append("<");
                for (int i = 0; i < typeArguments.size(); i++)
                {
                    if (i != 0)
                    {
                        result.append(", ");
                    }

                    // NOTE: Recursively resolve the types
                    typeToString(typeArguments.get(i), result, innerClassSeparator);
                }

                result.append(">");
            }

            return null;
        }

        @Override
        public Void visitPrimitive(PrimitiveType primitiveType, Void v) { ... }

        @Override
        public Void visitArray(ArrayType arrayType, Void v) { ... }

        @Override
        public Void visitTypeVariable(TypeVariable typeVariable, Void v) 
        {
            result.append(typeVariable.asElement().getSimpleName());
            return null;
        }

        @Override
        public Void visitError(ErrorType errorType, Void v) { ... }

        @Override
        protected Void defaultAction(TypeMirror typeMirror, Void v) { ... }
    }, null);
}

我正忙于我自己的生成类扩展的项目。 Dagger 方法适用于复杂情况,包括通用内部类。我有以下结果:

我的带有要扩展字段的测试类:

public class AnnotationTest
{
    ...

    public static class A
    {
        @MyAnnotation
        private Set<B<Integer>> _bs;
    }

    public static class B<T>
    {
        private T _value;
    }
}

在处理器为_bs 字段提供的Element 上调用Dagger 方法:

accessor.type = DaggerUtils.typeToString(element.asType());

生成的源(当然是自定义的)。请注意令人敬畏的嵌套泛型类型。

public java.util.Set<AnnotationTest.B<java.lang.Integer>> AnnotationTest.A.getBsGenerated()
{
    return this._bs;
}

编辑:调整概念以提取第一个通用参数的 TypeMirror,否则为 null:

public static TypeMirror getGenericType(final TypeMirror type)
{
    final TypeMirror[] result = { null };

    type.accept(new SimpleTypeVisitor6<Void, Void>()
    {
        @Override
        public Void visitDeclared(DeclaredType declaredType, Void v)
        {
            List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
            if (!typeArguments.isEmpty())
            {
                result[0] = typeArguments.get(0);
            }
            return null;
        }
        @Override
        public Void visitPrimitive(PrimitiveType primitiveType, Void v)
        {
            return null;
        }
        @Override
        public Void visitArray(ArrayType arrayType, Void v)
        {
            return null;
        }
        @Override
        public Void visitTypeVariable(TypeVariable typeVariable, Void v)
        {
            return null;
        }
        @Override
        public Void visitError(ErrorType errorType, Void v)
        {
            return null;
        }
        @Override
        protected Void defaultAction(TypeMirror typeMirror, Void v)
        {
            throw new UnsupportedOperationException();
        }
    }, null);

    return result[0];
}

【讨论】:

    【解决方案2】:

    看起来有几个问题。一,isAssignable() 没有按预期工作。其次,在上面的代码中,您试图获取 Set 类型 (T) 的泛型参数,而不是变量声明 (Role)。

    不过,下面的代码应该能证明你需要什么:

    @SupportedAnnotationTypes({ "xxx.MyAnnotation" })
    @SupportedSourceVersion(SourceVersion.RELEASE_6)
    public class MongoDocumentAnnotationProcessor extends AbstractProcessor {
        @Override
        public synchronized void init(ProcessingEnvironment processingEnv) {
            super.init(processingEnv);
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            if (roundEnv.processingOver() || annotations.size() == 0) {
                return false;
            }
            for (Element element : roundEnv.getRootElements()) {
                if (element.getKind() == ElementKind.CLASS && isAnnotatedWithMongoDocument(element)) {
                    System.out.println("Running " + getClass().getSimpleName());
                    for (VariableElement variableElement : ElementFilter.fieldsIn(element.getEnclosedElements())) {
                        if(variableElement.asType() instanceof DeclaredType){
                            DeclaredType declaredType = (DeclaredType) variableElement.asType();
    
                            for (TypeMirror typeMirror : declaredType.getTypeArguments()) {
                                System.out.println(typeMirror.toString());
                            }
                        }
                    }
                }
            }
            return true;  //processed
        }
    
        private boolean isAnnotatedWithMongoDocument(Element element) {
            return element.getAnnotation(MyAnnotation.class) != null;
        }
    }
    

    这段代码应该输出:

    xxx.Role
    

    【讨论】:

      【解决方案3】:

      所有其他答案,同时有很多优点。不要真正向您展示您遇到的问题及其解决方案。

      你的代码有问题

      TypeElement collectionType = elementUtils.getTypeElement("java.util.Collection");
      if (typeUtils.isAssignable(typeElement.asType(), collectionType.asType())) {
      ...
      

      您的类型不是扩展java.util.Collection,而是扩展java.util.Collection&lt;*&gt;。让我们重写上面的代码块来反映这一点:

      WildcardType WILDCARD_TYPE_NULL = this.typeUtils.getWildcardType(null, null);
      final TypeElement collectionTypeElement = this.elementUtils.getTypeElement(Collection.class.getName());
      TypeMirror[] typex = {WILDCARD_TYPE_NULL};
      DeclaredType collectionType=this.typeUtils.getDeclaredType(collectionTypeElement, typex);
      if (typeUtils.isAssignable(typeElement.asType(), collectionType)){ 
       ...
      

      这应该可以正常工作

      【讨论】:

        【解决方案4】:

        使用 Java 11,您可以将 TypeMirror 转换为 Type.ClassType 这段代码

        // classToIntrospect is a TypeMirror of java.util.List<it.firegloves.sragen.Dog>
        (ClassType)classToIntrospect
        

        将在

        【讨论】:

          猜你喜欢
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-05
          • 2012-04-20
          • 2017-11-06
          • 1970-01-01
          相关资源
          最近更新 更多