繁体   English   中英

如何在Java“注释处理器”中处理泛型?

[英]How to handle generics inside a Java “annotation processor”?

我之前问过一个例子“注释处理器”,它会为一个接口生成一个代理/代理,但没有得到答案,也没有在互联网上找到任何东西,所以我自己做了。

到目前为止它运行良好,直到我尝试在超级界面中使用泛型。 如果我在带注释的界面中使用泛型,它可以正常工作(更多是偶然而非设计)。 但是,如果带注释的接口扩展了另一个采用泛型类型参数的接口,则该参数不会“绑定”到注释接口在扩展超级接口时使用的类型。 例:

public interface TestFragment<E> {
    void test(E dummy);
}
@CreateWrapper
public interface TestService extends TestFragment<String> {
    double myOwnMethod();
}

这会产生:

// ...
public void test(final E dummy) {
    wrapped.test(dummy);
}
// ...

而不是正确的:

// ...
public void test(final String dummy) {
    wrapped.test(dummy);
}
// ...

在生成的方法中生成参数的代码如下所示:

int count = 0;
for (VariableElement param : method.getParameters()) {
    if (count > 0) {
        pw.print(", ");
    }
    count++;
    pw.printf("final %s %s", param.asType().toString(),
        param.getSimpleName().toString());
}

有没有办法做到这一点?

如果您按照Ryan Walls建议使用asMemberOf,这可能非常简单

ExecutableType methodType = (ExecutableType) typeUtil
      .asMemberOf((DeclaredType) theAnnotatedClass.asType(), method);

int count = 0;
for (VariableElement param : method.getParameters()) {
    if (count > 0) {
        pw.print(", ");
    }
    TypeMirror actualParamType = methodType.getParameterTypes().get(count);
    pw.printf("final %s %s", actualParamType.toString(),
        param.getSimpleName().toString());
    count++;
}

你需要的是替换,给定类型变量的映射到类型参数。 在这种情况下, E->String String替换任何类型的任何E

javax.lang.model.util.Types没有这样的支持,你需要自己动手。 基本上

void print(TypeMirror type, Map<TypeVariable,TypeMirror> substitution)

    if(substitution.containsKey(type)) // type is a var, E
        print( substitution.get(type) ); // String

    else if(type instanceof DeclaredType) // e.g. List<E>
        print( type.asElement().getSimpleName() );  // List
        for(TypeMirror arg : type.getTypeArguments() ) // E
            print(arg, substitution)

    etc. something like that

复制粘贴我的原始答案

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

Dagger DI项目根据Apache 2.0许可证授权,并包含一些用于处理注释处理器中类型的实用方法。

特别是,可以在GitHub( Util.java )上完整地查看Util类,并定义一个方法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;
    }
}

在处理器为_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];
}

暂无
暂无

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

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