9

我正在编写一个工具,它使用注释处理器根据带注释的类的方法的返回类型生成源代码。返回类型始终A是定义类型变量的接口的某个子类型(接口或类) T

interface A<T>{T m();};

我想找到方法m()返回值类型变量的类型参数T

返回类型由注释处理器表示为一个javax.lang.model.type.TypeMirror实例。最简单的情况是A<T>直接返回。

@SomeAnnotation
class SomeClass{
    A<T> x();
}

要找出的处理器代码T非常简单。(我将在这里转换而不是使用访问者 API 以保持代码简单。)

DeclaredType type = (DeclaredType) typeMirror;
TypeMirror t = type.getTypeArguments().get(0);

TypeMirror返回类型的 是 a并且javax.lang.model.type.DeclaredTypeT第一个类型参数。结果t是一个javax.lang.model.type.TypeVariablefor T。这同样适用于具体的返回类型A<B>B是某种类型:)interface B{}。的结果t是一个DeclaredType表示B

其他结果类型开始变得复杂:

interface Subtype<T> extends A<T>{}
interface Concrete extends A<B>{};
interface Multiple<B,T> extends A<T>{}
interface Bounds<T extends B> extends A<T>{}
interface Hierarchy extends Concrete{}
Subtype<B>          -> DeclaredType B
Subtype<T>          -> TypeVariable T
Concrete            -> DeclaredType B
Multiple<B,T>       -> TypeVariable T or DeclaredType B depeding on Multiple
Multiple<B,B>       -> TypeVariable B
<T extends B> A<T>  -> TypeVariable T with super class bound B
Bound<B>            -> DeclaredType B
Bound<C>            -> DeclaredType C (subtype of B)
Hierarchy           -> TypeVariable T

有没有办法在T不镜像整个 java 类型系统的情况下找到正确的类型参数?

4

3 回答 3

6

看看http://docs.oracle.com/javase/6/docs/api/javax/lang/model/util/Types.html#asMemberOf%28javax.lang.model.type.DeclaredType,%20javax.lang .model.element.Element%29

我用它来解决这个问题,并在这个拉取请求中为 WsDoc 项目贡献了解决方案:https ://github.com/versly/wsdoc/pull/7

我做了这样的事情:

      Type.MethodType methodType = (Type.MethodType) processingEnv.getTypeUtils().asMemberOf(declaredTypeThatExtendsSomeGenericParent, methodToGetReturnTypeForAsExecutableElement);
      TypeMirror type = methodType.getReturnType();
于 2013-01-15T18:58:02.633 回答
3
 public AnnotationProcessor getProcessorFor(
            Set<AnnotationTypeDeclaration> atds,
            AnnotationProcessorEnvironment env) {
        return new SomeAnnotationProcessor(env);
    }

    private static class SomeAnnotationProcessor implements AnnotationProcessor {
        private final AnnotationProcessorEnvironment env;

        SomeAnnotationProcessor(AnnotationProcessorEnvironment env) {
            this.env = env;
        }

        public void process() {
            for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) {
                System.out.println("in class: " + typeDecl);
                typeDecl.accept(getDeclarationScanner(
                        new SomeClassVisitor(), NO_OP));
            }
        }

        private static class SomeClassVisitor extends SimpleDeclarationVisitor {
            @Override
            public void visitMethodDeclaration(
                    MethodDeclaration methodDeclaration) {
                System.out.println("visiting method: "+methodDeclaration + " -> "+methodDeclaration.getReturnType());
                methodDeclaration.getReturnType().accept(new SomeTypeVisitor());
            }
        }
    }

    private static class SomeTypeVisitor implements TypeVisitor {

        public void visitClassType(ClassType classType) {           
            System.out.println("classType: " + classType + " -> "+classType.getClass());                
        }

        @Override
        public void visitInterfaceType(InterfaceType interfaceType) {
            Types types = annotationProcessorEnvironment.getTypeUtils();
            TypeDeclaration typeDeclaration = annotationProcessorEnvironment
                    .getTypeDeclaration("A");           
            Collection<InterfaceType> superinterfaces = interfaceType
                    .getSuperinterfaces();                      
            System.out.println("interfaceType: " + interfaceType + " -> "
                    + superinterfaces);
            DeclaredType typeOfA = types.getDeclaredType(typeDeclaration);
            boolean isSubTypeOfA = types.isSubtype(interfaceType, typeOfA);         
            if (isSubTypeOfA) {
                findTypeVariable(types, superinterfaces, typeOfA);
            }
            Iterator<TypeMirror> iterator = interfaceType
                    .getActualTypeArguments().iterator();
            while (iterator.hasNext()) {
                TypeMirror next = iterator.next();
                next.accept(new SomeTypeVisitor());
            }
        }

        public void visitTypeVariable(TypeVariable typeVariable) {          
            System.out.println("typeVariable: "
                    + typeVariable.getDeclaration() + " -> "+typeVariable.getClass());              
        }

        private void findTypeVariable(Types types,
                Collection<InterfaceType> superinterfaces, DeclaredType typeOfA) {
            for (InterfaceType superInterface : superinterfaces) {
                TypeMirror erasure = types.getErasure(superInterface);
                if (erasure.equals(typeOfA)) {
                    System.out.println("true, "+superInterface.getActualTypeArguments());
                } else {
                    System.out.println("false: " + typeOfA + " =!= "
                            + erasure);
                    findTypeVariable(types, superInterface.getSuperinterfaces(), typeOfA);
                }
            }
        }


    }
于 2009-09-09T13:36:41.487 回答
3

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

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

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

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;
    }
}

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

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];
}
于 2013-10-28T18:34:25.993 回答