Help us understand the problem. What is going on with this article?

IDE で注釈処理を動かすときにRetentionがSOURCEのアノテーションの情報が取れないことがある

More than 1 year has passed since last update.

IDE で注釈処理を動かすときに、 @Retention(RetentionPolicy.SOURCE) で定義しているアノテーションの情報が、特定の条件下で取得できないことがある。

環境

OS

Windows 10

Java

openjdk 10.0.2

Eclipse

Photon (4.8.0)Pleiades

IntelliJ

IntelliJ IDEA 2018.2.3 (Community Edition)

Gradle

Gradle 4.10

症状

ソースコード

プロジェクト構成
|-foo/
| `-src/main/java/
|   `-sample/foo/
|     |-Fizz.java
|     `-Buzz.java
|
|-bar/
| |-src/main/java/
| | `-sample/bar/
| |   |-MyRuntimeRetention.java
| |   |-MyClassRetention.java
| |   |-MySourceRetention.java
| |   `-MyProcessor.java
| |
| `-src/main/resources/
|   `-META-INF/services
|     `-javax.annotation.processing.Processor
|
|-build.gradle
`-settings.gradle
  • foo, bar のマルチプロジェクト
  • bar の方が注釈処理を実装したプロジェクト
  • foobar の注釈処理を利用しているプロジェクト
  • MyRuntimeRetention.java, MyClassRetention.java, MySourceRetention.java は、それぞれ自作のアノテーションで Retention に名前に対応する値を設定している
MyRuntimeRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeRetention {
}
MyClassRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface MyClassRetention {
}
MySourceRetention.java
package sample.bar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MySourceRetention {
}
  • MyProcessor.java が注釈処理を実装したクラス
MyProcessor.java
package sample.bar;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;

public class MyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Elements elements = this.processingEnv.getElementUtils();

        if (!annotations.isEmpty()) {
            this.printAnnotations(elements.getTypeElement("sample.foo.Fizz"));
            this.printAnnotations(elements.getTypeElement("sample.foo.Buzz"));
        }

        return true;
    }

    private void printAnnotations(TypeElement element) {
        element.getAnnotationMirrors().forEach(annotation -> {
            this.processingEnv.getMessager().printMessage(Kind.NOTE, element.getSimpleName() + " : " + annotation);
        });
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_10;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of(MySourceRetention.class.getCanonicalName());
    }
}
  • Fizz, Buzz クラスの TypeElement を取得して、クラスに設定されているアノテーションを一覧で出力している
Fizz.java
package sample.foo;

import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;

@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Fizz {
}
Buzz.java
package sample.foo;

import sample.bar.MyClassRetention;
import sample.bar.MyRuntimeRetention;
import sample.bar.MySourceRetention;

@MyRuntimeRetention
@MyClassRetention
@MySourceRetention
public class Buzz {
}
  • foo プロジェクトの Fizz, Buzz は、クラスに3つの自作アノテーションを設定している

実施する処理

この状態で Fizz クラスだけを修正してコンパイルを実行する。
すると、 Gradle で実行したときと IDE (Eclipse, IntelliJ) で実行したときで異なる結果になる。

Gradle で動かしたとき

> gradle :foo:compileJava

注意:Fizz : @sample.bar.MyRuntimeRetention
注意:Fizz : @sample.bar.MyClassRetention
注意:Fizz : @sample.bar.MySourceRetention
注意:Buzz : @sample.bar.MyRuntimeRetention
注意:Buzz : @sample.bar.MyClassRetention
注意:Buzz : @sample.bar.MySourceRetention
  • 普通にすべてのアノテーションの情報が取得できている

Eclipse で動かしたとき

apt.jpg

  • Fizz クラスのアノテーションはすべて取得できている
  • しかし、 Buzz クラスのアノテーションは @MySourceRetention が取得できていない

IntelliJ で動かしたとき

apt.jpg

  • こちらも Eclipse と同じで、 Buzz クラスのアノテーション情報から @MySourceRetention が取得できていない

Buzz も修正すると IDE でも取得できる

  • Fizz クラスだけでなく Buzz クラスも修正してからビルドを実行して注釈処理を動かす
  • すると、次のような結果になる

Eclipse

apt.jpg

  • Buzz に設定された @MySourceRetention も取得できている

IntelliJ

apt.jpg

  • こちらも同様で、 @MySourceRetention を取得できている

何が起こったか推測

  • RetentionSOURCE にした場合、アノテーションの情報はクラスファイルには残らない
  • 修正していない場合に SOURCE 指定したアノテーションの情報だけが取得できていなかったことを鑑みるに、おそらく IDE (Eclipse, IntelliJ) で注釈処理が動いた場合は、修正が入っていないクラスの情報は以前のコンパイル結果(class ファイル)が利用されている気がする
  • ちなみに、 NetBeans は試していない

結論(推測)

  • IDE (Eclipse, IntelliJ) で注釈処理が動くとき、修正が入っていないクラスに設定されている RetentionSOURCE のアノテーションの情報は取得できないっぽい
  • 理由はおそらく、 IDE は修正が入っていないクラスの情報を以前のコンパイル結果(class ファイル)から取得しているっぽいから
  • 自作で注釈処理を書くときは、 RetentionSOURCE のアノテーションの扱いには注意が必要
  • アノテーションを自作しているのであれば、 RetentionCLASS 以上にしておくのが良さげ
opengl-8080
ただのSE。Java好き。
tis
創業40年超のSIerです。
https://www.tis.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away