【Java】 アノテーション ~ Annotation ~

■ はじめに

 * C# でいう「カスタム属性」は、Javaでは「アノテーション(Annotation)」という

■ サンプル

 * ただ、作っても面白くないので、C#の時に作成した
 (下記の関連記事「属性(アトリビュート) ~カスタム属性~」を参照のこと)
   Enumを利用したメッセージ処理を作成する
 * また、文言自体はファイルから取得してくるようにする
 (下記の関連記事「国際化対応 ~ ResourceBundle ~」を参照のこと)

MessageAnnotation.java

アノテーション
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;

//★重要★
//Enumに使用する場合に、RetentionPolicy.RUNTIMEでないと、Annotationが取得できない
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MessageAnnotation {
  public static final String DefaultMessageKey = "";
  public static final int DefaultMessageId = -1;

  public String MessageKey() default MessageAnnotation.DefaultMessageKey;

  public int MessageId() default MessageAnnotation.DefaultMessageId;
}

MessageType.java

Enum(アノテーション適用)
public enum MessageType {
  @MessageAnnotation(MessageKey = "Sample.Error1", MessageId = 1)
  MessageError,
  @MessageAnnotation(MessageKey = "Sample.Warning1", MessageId = 2)
  MessageWorning,
}

MessageManager.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.PropertyResourceBundle;

public class MessageManager {
  private PropertyResourceBundle resourceBundle;

  public MessageManager(String fileName) throws IOException {
    FileInputStream stream = null;
    try {
      File file = new File(fileName);
      stream = new FileInputStream(file);
      this.resourceBundle = new PropertyResourceBundle(stream);
    } catch (Exception ex) {
      ex.printStackTrace();
    } finally {
      if (stream != null) {
        stream.close();
      }
    }
  }

  public String getMessage(MessageType message, Object... parameters) {
    String returnValue = MessageAnnotation.DefaultMessageKey;
    try {
      MessageAnnotation annotation = this.getMessageAnnotation(message);
      String messageKey = annotation.MessageKey();
      String messageFromFile = this.resourceBundle.getString(messageKey);
      returnValue = String.format(messageFromFile, parameters);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return returnValue;
  }

  // アノテーションの取得
  private MessageAnnotation getMessageAnnotation(MessageType message) {
    MessageAnnotation messageAnnotation = null;
    try {
      Field messageTypeField = MessageType.class.getField(message.name());
      messageAnnotation = messageTypeField.getAnnotation(MessageAnnotation.class);
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return messageAnnotation;
  }
}

Main.java

呼び出し側
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    try {
      MessageManager messageManager = new MessageManager("Message.properties");
      System.out.println(messageManager.getMessage(MessageType.MessageError));
      System.out.println(messageManager.getMessage(MessageType.MessageWorning, "Testing"));
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Message.properties

文言の元になるファイル(【キー】=【文言】。プロジェクトの直下に作成する。
Sample.Error1=This is Error.
Sample.Warning1=This is Warning for %s.

出力結果

Sample.javaを実行した結果
This is Error.
This is Warning for Testing.

アノテーションあれこれ

コンパイル時にアノテーションを読み込んで処理するには...

 * 「Pluggable Annotation Processing API」を使う
  => 詳細は、以下の関連記事を参照のこと
https://blogs.yahoo.co.jp/dk521123/37774601.html

対象のアノテーションが付与されているかを判定する

* SampleAnnotation.java(アノテーション)
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//★重要★
//RetentionPolicy.RUNTIMEでないと、判定できない
@Retention(RetentionPolicy.RUNTIME)
public @interface SampleAnnotation {
}
* ClassWithAnnotation.javaアノテーションを付与したクラス)
@SampleAnnotation
public class ClassWithAnnotation {
}
* ClassWithAnnotation.javaアノテーションを付与してないクラス)
public class ClassWithoutAnnotation {
}
* Main.java(呼び出し側)
public class Main {
  public static void main(String[] args) {
    ClassWithAnnotation class1 = new ClassWithAnnotation();
    System.out.println("ClassWithAnnotationは、True : " + class1.getClass().isAnnotationPresent(SampleAnnotation.class));

    ClassWithoutAnnotation class2 = new ClassWithoutAnnotation();
    System.out.println(
        "ClassWithoutAnnotationは、なんもつけてないのでFalse : " + class2.getClass().isAnnotationPresent(SampleAnnotation.class));
  }
}
* 出力結果
ClassWithAnnotationは、True : true
ClassWithoutAnnotationは、なんもつけてないのでFalse : false

関連記事

自作のインターセプター

https://blogs.yahoo.co.jp/dk521123/33226901.html

Pluggable Annotation Processing API

https://blogs.yahoo.co.jp/dk521123/37774601.html

C#】属性(アトリビュート) ~カスタム属性~

https://blogs.yahoo.co.jp/dk521123/29483903.html

国際化対応 ~ ResourceBundle ~

https://blogs.yahoo.co.jp/dk521123/32519372.html