Откройте для себя класс вызова метода в обработчике аннотаций для Java.

Я пишу некоторые инструменты для нашей системы сборки, чтобы обеспечить соблюдение некоторых строгих соглашений о вызовах для методов, принадлежащих классам, содержащим определенные аннотации.

Я использую API дерева компилятора...

Что мне интересно, так это то, что при обходе «дерева» вы можете указать тип класса/интерфейса для MethodInvocation.

Я подклассифицирую TreePathScanner с помощью:

@Override
public Object visitMethodInvocation(MethodInvocationTree node, Trees trees) {

}

Я надеюсь, что есть способ указать тип класса (или интерфейса), для которого вы пытаетесь вызвать метод. Я иду об этом неправильно? Спасибо за любые идеи...


person Vincent    schedule 30.06.2009    source источник


Ответы (1)


Здесь есть несколько проблем. Вы можете либо заинтересоваться знанием типа Java получателя вызова метода, либо просто узнать, какой класс метода вызывается. Информация о Java более информативна, поскольку она также дает вам общие типы, например. List<String> в то время как Elements предоставит вам только класс, например. List<E>.

Получение элемента

Чтобы получить элемент класса, для которого вызывается метод, вы можете сделать следующее:


  MethodInvocationTree node = ...;
  Element method =
        TreeInfo.symbol((JCTree)node.getMethodSelect());
  TypeElement invokedClass = (TypeElement)method.getEnclosingElement();

Угловые случаи:

1. invokedClass может быть суперклассом типа приемника. Таким образом, запуск фрагмента на new ArrayList<String>.equals(null) вернет AbstractList, а не ArrayList, поскольку equals() реализован в AbstractList, а не ArrayList.

2. При обработке вызовов массива, например. new int[].clone() вы получите TypeElement класса Array.

Получение фактического типа

Чтобы получить тип, нет прямого способа определить тип получателя. Существует некоторая сложность в обработке вызовов методов во внутренних классах, где получатель не указан явно (например, в отличие от OuterClass.this.toString()). Вот пример реализации:


  MethodInvocationTree node = ...;
  TypeMirror receiver;
  if (methodSel.getKind() == Tree.Kind.MEMBER_SELECT) {
    ExpressionTree receiver = ((MemberSelectTree)methodSel).getExpression();
    receiverType = ((JCTree)receiver).type;
  } else if (methodSel.getKind() == Tree.Kind.IDENTIFIER) {
    // need to resolve implicit this, which is described in
    //  JLS3 15.12.1 and 15.9.2

    // A bit too much work that I don't want to work on now
    // Look at source code of
    //   Attr.visitApply(JCMethodInvocation)
    //   resolveImplicitThis(DiagnosticPosition, Env, Type)
  } else
    throw new AssertionError("Unexpected type: " + methodSel.getKind());

Примечание:

К сожалению, тип receiver должен быть TypeMirror, а не DeclaredType. При вызове new int[5].clone() receiver будет ArrayType из int[], что более информативно, чем предыдущий метод.

Запускаем

Оба предыдущих метода требуют, чтобы компилятор разрешил информацию о типе для классов. В обычных обстоятельствах компилятор разрешает только типы для объявлений методов, но не тела. Следовательно, описанные ранее методы вместо этого будут возвращать null.

Чтобы компилятор разрешил информацию о типе, вы можете сделать это одним из следующих способов:

1. Используйте класс AbstractTypeProcessor, который только что был добавлен в репозиторий компилятора для JDK 7. Ознакомьтесь с работой на JSR 308 и их компилятор. Хотя работа ведется в основном над аннотированными типами, она может быть полезна для. Компилятор позволяет вам использовать предоставленный класс обратно совместимым образом с Java 5.

Этот подход позволяет вам писать процессоры, которые вызываются точно так же, как ваши текущие процессоры.

2. Вместо этого используйте JavacTask и вызовите JavacTask.analyze(). Посмотрите на основной метод этот тест javac, чтобы узнать, как вызвать посетителя в классах.

Такой подход делает ваш процессор более похожим на инструмент анализа, чем на плагин к компилятору, поскольку вам нужно будет вызывать его напрямую, а не как обычный процесс.

person notnoop    schedule 30.06.2009
comment
Спасибо за ответ. Однако строка: Element method = TreeInfo.symbol((JCTree)node.getMethodSelect()); возвращает ноль для меня. TypeMirror m = деревья.getTypeMirror(путь); также возвращает null, как и TypeElement e = (TypeElement)trees.getElement(path); Я совершаю ошибку новичка и забываю что-то инициализировать, или вызываю/переопределяю неправильный метод, или создаю неправильные подклассы? В очередной раз благодарим за помощь... - person Vincent; 01.07.2009
comment
Я только что обновил запись, чтобы объяснить, почему методы возвращают значение null и как решить эту проблему. - person notnoop; 01.07.2009
comment
К вашему сведению, AbstractTypeProcessor больше не доступен в сборках JDK 7, так как поддержка JSR 308 была прекращена. - person Jesse Glick; 15.11.2010