web-dev-qa-db-ja.com

アノテーションを実装するためのユースケース

アノテーションを実装するための有効なユースケースは何ですか?

主にアノテーションベースの構成システムを設計する場合、コード生成またはプログラム構成用のアノテーションを実装するクラスを作成する必要がある場合があります。

別の方法として、注釈に含まれるデータをDTOにミラーリングする必要がありますが、これはオーバーヘッドのようです。

次に例を示します。

public enum IDType {
    LOCAL,
    URI,
    RESOURCE;
}

@Documented
@Target( { METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Id {
    /**
     * @return
     */
    IDType value() default IDType.LOCAL;
}

実装で

public class IdImpl implements Id{

    private final IDType idType;

    public IdImpl(IDType idType){
        this.idType = idType;
    }

    @Override
    public IDType value() {
        return idType;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }

}

これについてコンパイラの警告が表示されますが、多くのユースケースで有効なツールのようです。

上記の例の警告は次のとおりです。

注釈タイプIdは、IdImplのスーパーインターフェイスとして使用しないでください。

編集:

この例を Guice から見つけました:

bind(CreditCardProcessor.class)
    .annotatedWith(Names.named("Checkout"))
    .to(CheckoutCreditCardProcessor.class);

これを参照してください NamesのJavadoc

この制限が存在する理由や他のユースケースを念頭に置いている情報はありますか?

37

私は実際にそれを使用したことはありませんが、注釈の代わりにクラスを使用できるということがわかります。

人工的な例を作成しましょう。ドキュメントジェネレータがあるとします。指定されたクラスから@Docuアノテーションを読み取り、description属性を出力します。このような:

import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;
import Java.util.ArrayList;
import Java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

プリント:

This is a static class!  
This is another static class!

ここで達成したいのは、クラスに静的な注釈を付けるだけでなく、実行時情報をドキュメントに追加できることです。ほとんどの場合、@Docuアノテーションを使用できますが、特別なドキュメントが必要な場合もあります。一部のメソッドのパフォーマンスドキュメントを追加したい場合があります。これを行うには、クラスにアノテーションを実装させます。ジェネレーターは最初にアノテーションをチェックし、存在しない場合は、クラスがアノテーションを実装しているかどうかをチェックします。含まれている場合は、クラスを注釈のリストに追加します。

このように(ジェネレーターには2行の追加コードのみ):

import Java.lang.annotation.Annotation;
import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class,
                DynamicClass.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
            else if (Arrays.asList(c.getInterfaces()).contains(Docu.class))
                result.add((Docu) c.newInstance());
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

class DynamicClass implements Docu {

    public DynamicClass() {
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
            // ignore exception to make debugging a little harder
        }
    }

    @Override
    public String description() {
        long millis = System.currentTimeMillis();
        new DynamicClass();
        millis = System.currentTimeMillis() - millis;
        return "This is a dynamic class. I run on "
                + System.getProperty("os.name")
                + ". The construction of an instance of this class run for "
                + millis + " milliseconds.";
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Docu.class;
    }

}

出力は次のとおりです。

This is a static class!  
This is another static class!  
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds.

アノテーションの代わりにクラスを使用できるため、コードジェネレーターをそれほど変更する必要はありません。

他の例は、構成として注釈またはXMLを使用するフレームワークです。注釈を処理するプロセッサが1つある場合があります。 XMLを構成として使用する場合、アノテーションを実装するクラスのインスタンスを生成でき、プロセッサーは1回の変更なしでそれらを処理します。 (もちろん、同じ効果を達成する方法は他にもありますが、これはそれを行う1つの方法です)

21
Arne Deutsch

JAXBIntroductions は良い例です。XMLファイルを使用してJAXBアノテーションを構成できます。 2つの主なユースケースが思い浮かびます。ソースアクセスがないクラスの構成と、1つのクラスの異なる構成です。

一般に、アノテーションを動的にインスタンス化してフレームワークに渡すことは、一般的に良いユースケースだと思います。しかし、あなたがこのフレームワークの設計者であるなら、私は確かによく考え直します。

4
sfussenegger

注釈を作成したときに使用し、注釈が省略されたときにデフォルトを提供することで、その使用をオプションにしたい。これは、ライブラリが新しいアノテーションを導入し、ライブラリに下位互換性を維持したい場合に発生する可能性があります。

この例では、BeanBは古いバージョンのライブラリに対してソースコードで記述されているため、そのようなクラスを識別するときにデフォルトを使用する必要があります。

@Id
class BeanA {}

// No annotation
class BeanB {}

デフォルトの実装。

private static final Id DEFAULT_ID = new Id() {

    @Override
    public IDType value() {
        return IDType.LOCAL;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }
};

処理;

Id beanId = (bean.getClass().isAnnotationPresent(Id.class))
    ? bean.getClass().getAnnotation(Id.class)
    : DEFAULT_ID;
0
Foumpie