简体   繁体   English

在Java 6注释处理器中查找方法返回类型的类型参数

[英]Find type parameter of method return type in Java 6 annotation processor

I'm writing a tool that uses the annotation processor to generate source code depending on the return type of methods of an annotated class. 我正在编写一个工具,它使用注释处理器根据注释类的方法的返回类型生成源代码。 The return type is always some subtype (interface or class) of an interface A that defines a type variable T . 返回类型始终是定义类型变量T的接口A某个子类型(接口或类)。

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

I would like to find the type parameter for the method m() return value type variable T . 我想找到方法m()返回值类型变量T类型参数。

The return type is represented by the annotation processor as a javax.lang.model.type.TypeMirror instance. 返回类型由注释处理器表示为javax.lang.model.type.TypeMirror实例。 The simplest case is to return A<T> directly. 最简单的情况是直接返回A<T>

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

The processor code to find out T is quite simple. 找出T的处理器代码非常简单。 (I'll cast instead of using the visitor API here to keep the code simple.) (我会在这里投射而不是使用访问者API来保持代码简单。)

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

The TypeMirror of the return type is a javax.lang.model.type.DeclaredType and T is the first type argument. 所述TypeMirror返回类型是javax.lang.model.type.DeclaredTypeT是第一类型参数。 The result t is a javax.lang.model.type.TypeVariable for T . 结果tTjavax.lang.model.type.TypeVariable The same works for a concrete return type A<B> ( B is some type: interface B{} ). 对于具体的返回类型A<B>B是某种类型: interface B{} )也是如此。 The result for t is a DeclaredType representing B . t的结果是表示BDeclaredType

Things start to get complicated with other result types: 其他结果类型开始变得复杂:

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

Is there a way to find the correct type parameter for T without mirroring the whole java type system? 有没有办法在不镜像整个java类型系统的情况下为T找到正确的类型参数?

Have a look at 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 看看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%

I used it to solve this problem and contributed the solution to the WsDoc project in this pull request: https://github.com/versly/wsdoc/pull/7 我用它来解决这个问题,并在这个拉取请求中为WsDoc项目提供了解决方案: https//github.com/versly/wsdoc/pull/7

I did something like this: 我做了这样的事情:

      Type.MethodType methodType = (Type.MethodType) processingEnv.getTypeUtils().asMemberOf(declaredTypeThatExtendsSomeGenericParent, methodToGetReturnTypeForAsExecutableElement);
      TypeMirror type = methodType.getReturnType();
 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);
                }
            }
        }


    }

This seems to be a common question so, for those arriving from Google: there is hope. 对于那些来自谷歌的人来说,这似乎是一个常见的问题:有希望。

The Dagger DI project is licensed under the Apache 2.0 License and contains some utility methods for working with types in an annotation processor. Dagger DI项目根据Apache 2.0许可证授权,并包含一些用于处理注释处理器中类型的实用方法。

In particular, the Util class can be viewed in full on GitHub ( Util.java ) and defines a method public static String typeToString(TypeMirror type) . 特别是,可以在GitHub( Util.java )上完整地查看Util类,并定义一个方法public static String typeToString(TypeMirror type) It uses a TypeVisitor and some recursive calls to build up a string representation of a type. 它使用TypeVisitor和一些递归调用来构建类型的字符串表示。 Here is a snippet for reference: 这是一个参考片段:

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

I am busy with my own project which generates class extensions. 我忙于我自己的项目,它生成类扩展。 The Dagger method works for complex situations, including generic inner classes. Dagger方法适用于复杂情况,包括通用内部类。 I have the following results: 我有以下结果:

My test class with field to extend: 我的测试类有扩展字段:

public class AnnotationTest
{
    ...

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

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

Calling the Dagger method on the Element the processor provides for the _bs field: 在处理器为_bs字段提供的Element上调用Dagger方法:

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

The generated source (custom, of course). 生成的源(当然是自定义的)。 Note the awesome nested generic types. 请注意令人敬畏的嵌套泛型类型。

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

EDIT: adapting the concept to extract a TypeMirror of the first generic argument, null otherwise: 编辑:调整概念以提取第一个泛型参数的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];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM