Как обрабатывать дженерики внутри Java "процессора аннотаций"? - PullRequest
7 голосов
/ 29 сентября 2011

Ранее я запрашивал пример «процессора аннотаций», который генерировал бы прокси / делегат для интерфейса, но не получил ответа и не нашел ничего в Интернете, поэтому я сделал свой собственный.

До сих пор это работало хорошо, пока я не попытался использовать дженерики внутри суперинтерфейса.Если я использую дженерики в аннотированном интерфейсе, он работает нормально (скорее случайно, чем по замыслу).Но если аннотированный интерфейс расширяет другой интерфейс, который принимает параметр универсального типа, этот параметр не «привязан» к типу, который аннотированный интерфейс использует при расширении суперинтерфейса.Пример:

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

Есть ли способ сделать это?

Ответы [ 4 ]

3 голосов
/ 15 января 2013

Взгляните на 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

Может быть полезно.Я использовал это, чтобы решить очень похожую проблему.

2 голосов
/ 27 июля 2016

Это может быть довольно просто, если вы будете следовать предложению Райана Уолласа об использовании 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++;
}
2 голосов
/ 29 сентября 2011

То, что вам нужно, это подстановка, учитывая карту переменных типа для ввода аргументов. В этом случае E->String. Замените любой E любого типа на String

В 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
0 голосов
/ 28 октября 2013

Копия-вставка моего оригинального ответа :

Кажется, это общий вопрос, поэтому для тех, кто прибывает из Google: надежда есть.

Проект 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;
    }
}

Вызов метода Dagger на Element, который процессор предоставляет для поля _bs:

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

Сгенерированный источник (пользовательский, конечно). Обратите внимание на удивительные вложенные универсальные типы.

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

РЕДАКТИРОВАТЬ: адаптировать концепцию для извлечения TypeMirror из первого универсального аргумента, ноль в противном случае:

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