[Java] 21. アノテーション(Annotation)を使う方法


Study / Java    作成日付 : 2019/09/05 22:58:20   修正日付 : 2021/01/27 14:19:59

こんにちは。明月です。


この投稿はJavaのアノテーション(Annotation)を使う方法に関する説明です。


Javaのアノテーション(Annotation)とはクラスやメソッド、変数なのでメタデータ、データの定義を設定するデータという意味です。

つまり、アノテーションのデータはプログラム実装では影響がなくて、クラスやメソッド、変数を区分するためのデータだと言えます。


アノテーションはアットマーク(@)を利用して設定することができます。

// ラムダ式のインタフェースを設定するアノテーション
// メソッドを二つ以上に作ったらコンパイル側でエラーが発生する。
@FunctionalInterface
// インターフェース
interface LambdaExpression{
  // 抽象メソッド
  void run();
}
// クラス
class Example {
  // 再定義のアノテーション(Objectから継承)
  @Override
  public String toString() {
    return "Hello world";
  }
  // 使用禁止というアノテーション
  // 呼び出すことの実装したらWarning表示する。
  @Deprecated
  public void print() {
    System.out.println("print");
  }
  // 設定されているスタック領域でWarningをなくすアノテーション
  @SuppressWarnings("unused")
  public void test() {
    int aaa = 0;
  }
}

Javaでよく使うアノテーションはOverride、Deprecatedがあります。

Overrideはクラスを継承して再定義する時に設定するアノテーションです。Deprecatedはメソッドを削除すると既存に継承したクラスでエラーが発生するため、メソッドを削除もできなくて、ただ使用をお勧めしないのことをお知らせする時に使うアノテーションです。


@FunctionalInterfaceのアノテーションを使ったインタフェースは関数を二つに作成するとエラーが発生することを確認できます。

printの関数は取り消し線があります。@Deprecatedのアノテーションの影響で関数を呼び出すことをすればWarningメッセージが表示します。 testの関数はint aaa=0;がありますが、@SuppressWarningsが無ければ、unusedのWarningメッセージが出ます。


上のことはJavaで提供するアノテーションです。アノテーションは開発者が作成することもできます。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// クラスで使うアノテーション設定
@Target(ElementType.TYPE)
// アノテーション設定
@interface TestClassAnnotaion {
  // アノテーション属性(valueは属性設定省略が可能)
  public String value();
}
// メソッドで使うアノテーション設定
@Target(ElementType.METHOD)
// アノテーション設定
@interface TestMethodAnnotaion {
  // アノテーション属性
  public String name() default "no name";
}
// 変数で使うアノテーション設定	
@Target(ElementType.FIELD)
// アノテーション設定
@interface TestVariableAnnotaion {
  // アノテーション属性 (valueでもないし、default値が設定されてないなら、必須要求属性になる。)
  public String name();
}
//実行クラス
public class Example {
  // クラスにアノテーション設定
  @TestClassAnnotaion("test")
  class Node {
    // メソッドにアノテーション設定
    @TestMethodAnnotaion(name = "print")
    public void print() {
      // コンソールに出力
      System.out.println("Hello world");
    }
  }
  // 変数にアノテーション設定(name属性は必須)
  @TestVariableAnnotaion(name = "node")
  private Node node = null;
  // コンストラクタ
  public Example() {
    // Nodeインスタンスを生成
    node = new Node();
    // print関数を呼び出す。
    node.print();
  }
  // 実行関数
  public static void main(String... args) {
    // インスタンスを生成
    new Example();
  }
}


アノテーションはプログラム実行することでは影響がありません。ただ、メタデータとして様々なクラスと関数、変数を区分するための情報として使うことができます。

アノテーションを生成する時に、@Targetアノテーションは使う区分を設定、@Retentionは設定するポジションを設定することができます。

タイプ 説明
@Target
TYPE クラス、インタフェース、enumなどに設定
FIELD メンバー変数に設定
METHOD メソッドに設定
PARAMETER 関数のパラメータに設定
CONSTRUCTOR コンストラクタに設定
LOCAL_VARIABLE ロカール変数に設定
ANNOTATION_TYPE アノテーションに設定
PACKAGE パッケージに設定
TYPE_PARAMETER ジェネリックタイプに設定
PACKAGE すべてのところで設定可能
@Retention
SOURCE ソース上だけ使うメタデータ情報。
CLASS .classファイルにはあるが、Runtime(実行中)には見えない。
RUNTIME Runtime(実行中)にも参照可能。 Reflectionを通ってクラスやメソッド区分ができます。

その以外に@inheritedの設定はクラスを継承する時にアノテーションメタデータ情報も継承するという意味です。@DocumentedはJava docを生成する時にメタデータ情報を含めるというアノテーション情報です。

私の個人の考えはReflection機能ではなければ別にアノテーションを使うことがないと思います。Reflectionでアノテーションをよってクラスを探索やメソッドを探索することが必要なので、それのためには必要です。

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

// メソッドで使うアノテーション設定
@Target(ElementType.METHOD)
// Reflectionで認識するアノテーション
@Retention(RetentionPolicy.RUNTIME)
// アノテーション設定
@interface CallMethod {
  // アノテーション属性
  public String name() default "no name";
}
// クラス
class Example {
  // Runtimeアノテーション設定
  @CallMethod
  public void run1() {
    // コンソールに出力
    System.out.println("run1 call");
  }
  // アノテーション設定しない
  public void run2() {
    // コンソールに出力
    System.out.println("run2 call");
  }
  // Runtimeアノテーション設定
  @CallMethod
  public void run3() {
    // コンソールに出力
    System.out.println("run3 call");
  }
  // 実行関数
  public static void main(String[] args) throws Throwable {
    // インスタンスを生成
    Example ex = new Example();
    // Reflectionでメソッドを検索
    for(Method method : ex.getClass().getMethods()) {
      // CallMethodのアノテーションを取得
      CallMethod anno = method.getAnnotation(CallMethod.class);
      // 設定しなければnullになる。
      if(anno != null) {
        // CallMethodがあるメソッドを実行する。
        method.invoke(ex);
      }
    }
  }
}


上の例をみればReflectionでアノテーションが設定している関数だけ呼び出すことができます。

つまり、アノテーションでStrategyパターン(戦略パターン)、facade patternパターンを実装することができます。


その以外にEclipseみたいにIDEツールやJankinsのCIツールなどで使うためのアノテーションです。

ただ、コメントとしてアノテーションを作成するのは逆にソースの可読性を悪くなることがあるので、コメントと区分して使えばよいと思います。


ここまでJavaのアノテーション(Annotation)を使う方法に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿