【Java】アノテーション

#アノテーション
* クラス・インターフェースなどの型、メンバーに付与できるマーク
* **アプリ本来のロジックとは直接関係しない付随情報をコードに追加**できる
* 対象となる宣言の先頭に記述

##@Override
* 標準ライブラリで提供されるアノテーション
* 対象メソッドが**基底クラスのメソッドをオーバーライド**していることを宣言
* 引数のミスでオーバーライドできてない時もコンパイラが警告してくれる

```java
public class Person {
  private String firstName;
  private String lastName;

  public Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format("名前は、%s %s です。",
        this.lastName, this.firstName);
  }

  public String getLastName() {
    return this.lastName;
  }

  public String getFirstName() {
    return this.firstName;
  }
}
```

##@WebServlet
* サーブレットとは**サーバー環境でJavaアプリを実行するための基本技術**の一種
* サーブレットの基本的な構成情報
    * **urlPatterns**：サーブレットを呼ぶためのURL
    * **initParams**：サーブレットで利用できるパラメータ情報

##アノテーションのポイント
* **アノテーション入れ子が可能**
* **要素1個なら配列の{...}省略可能**
    * `urlPatterns = "/init-param"`
* **属性名valuesは省略可能**
    * @WebServlet (values="init-param")
    * @WebServlet ("init-param")
* **属性がない場合、()省略可能**
    * マーカーアノテーション（@Overrideなど）

#標準アノテーション
* @Override：基底クラスのメソッドを上書きしていることを宣言
* @Deprecated：クラスやメンバーなどが非推奨であることを宣言
* @SuppressWarnings：コンパイラー警告を抑制
* @SafeVarargs：可変長引数の型安全を宣言
* @FunctionalInterface：インターフェースを関数型インターフェースとして定義することを宣言

##@Deprecated
* クラス、メソッドなどが**非推奨であることを宣言**
* IntegerコンストラクタはJava9以降で非推奨（`since="9"`）
    * ドキュメンテーションでも記述
* コンパイルは通るが警告がでる

```java:Integer.java
/**
 * 中略
 * @deprecated
 * It is rarely appropriate to use this constructor. The static factory
 * {@link #valueOf(int)} is generally a better choice, as it is
 * likely to yield significantly better space and time performance.
 */

@Deprecated(since="9")
public Integer(int value) {
    this.value = value;
}

```

##@SuppressWarnings
* **特定範囲/種類のコンパイラー警告を非表示**
    * 他の本来確認すべき警告の見落としを防げる
    * 意図して警告対象のコードを利用しているという意思表示

```java
public class AnnotationBasic {

  public static Integer process() {
   @SuppressWarnings("deprecation")
    var i = new Integer(108);
   return i;
  }

  public static void main(String[] args) {
    System.out.println(process());
  }
}
```

##@SafeVarargs
* **可変長引数の型安全を宣言し、警告が表示されない**
    * ジェネリック型を可変長引数とすると警告が出る
* あくまでも型安全であることを開発者が認識してるという宣言

#自作アノテーション
* アノテーションは修飾子と違って自作できる
* **アノテーションは@interface命令で定義**
* **@interface配下でアノテーションで利用できる属性を宣言**
    * 以下の@ClassInfoではバージョン属性をversion属性、value属性で指定する
        * `@ClassInfo(version="2.1", description="アノテーションテスト")`
        * `@ClassInfo(version="2.1")`
* **アノテーション構成情報宣言するメタアノテーションを記述**
    * @Documented：javadocにアノテーション情報反映
    * @Target：アノテーションをどの要素に適用するか決める
    * @Inherited：アノテーションが派生クラスにも継承
    * @Retention：アノテーション情報をどのタイミングまで保持するか


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

//構成情報宣言
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
//@interface命令でアノテーション定義
public @interface ClassInfo {
  //アノテーションで利用できる属性を宣言
  String value() default "";
  String version() default "";
  String description() default "";
}
```

#リフレクションでアノテーション利用
* **リフレクション（Reflection）**：コードの実行中に型情報取得、操作する
* 以下では**Class.forNameメソッド**で指定した型情報をClassクラスで取得
* マーカーアノテーション：アノテーションが保持する情報（属性）ではなく**アノテーション存在そのものに関心がある**
    * **isAnnotationPresentメソッド**でアノテーション有無確認 

```java
package mypackage;

//ClassInfoアノテーション指定
@ClassInfo(version = "2.1", description = "アノテーションの動作テスト")
// @ClassInfo("2.1")
public class AnnotationClient {
  public static void main(String[] args) throws ClassNotFoundException {
    //Classオブジェクトを経由して配下のメンバー取得
    var clazz = Class.forName("mypackage.AnnotationClient");
    //getAnnotationメソッドでクラスに付与されたアノテーション取得
    var info = (ClassInfo)
 clazz.getAnnotation(ClassInfo.class);
    System.out.println("バージョン：" +
        (info.value().equals("") ? info.version() : info.value()));
    System.out.println("説明：" + info.description());
  }
}
```

#リフレクションのメソッド
##オブジェクト生成
* コンストラクタ経由でインスタンス生成
    * 呼び出すコンストラクタをClassクラスの**getConstructorメソッド**で生成
    * **newInstanceメソッド**で呼び出すことでオブジェクト生成
    * **Array.newInstanceメソッド**で配列生成
    * **setメソッド**で配列要素設定

```java
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

public class ReflectInstance {

  public static void main(String[] args) {
    try {
      //Fileクラス取得
      var clazz = File.class;

      //コンストラクタ経由でFileオブジェクト生成
      var c = clazz.getConstructor(String.class);
      var fl = c.newInstance("./data/data.txt");
      System.out.println(fl);

      //サイズ2の配列生成
      var list = (File[]) Array.newInstance(File.class, 2);
      Array.set(list, 0, fl);
      System.out.println(Arrays.toString(list));
    } catch (InstantiationException | IllegalAccessException |
        IllegalArgumentException | InvocationTargetException
        | NoSuchMethodException | SecurityException e) {
      e.printStackTrace();
    }
  }
}
```
##全メソッド取得
* **getMethodsメソッド**でFileクラスのPublicメソッド列挙
    * publicメソッドをMethodオブジェクトの配列として返す
* public以外も取りたいときは**getDeclaredMethodsメソッド**で全メソッド取得

```java
import java.io.File;

public class ReflectMethods {

  public static void main(String[] args) {
    //Fileクラス取得
    var str = File.class;
    //Fileクラス配下のpublicメソッドをgetNameでメソッド名列挙
    for (var m : str.getMethods()) {
      System.out.println(m.getName());
    }
  }
}
```

##メソッド実行
* **Methodオブジェクトを介して実行**
* **getMethodメソッド**で個別のメソッドを取得し、**invokeメソッド**で実行

```java
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectInvoke {

  public static void main(String[] args) {
    try {
      //Fileクラス取得
      var clazz = File.class;
      //Fileオブジェクト生成
      var f1 = clazz.getConstructor(String.class).newInstance("./data/data.txt");
      var f2 = clazz.getConstructor(String.class).newInstance("./data/sample.txt");
      //renameToメソッド取得、実行
      Method m = clazz.getMethod("renameTo", File.class);
      System.out.println(m.invoke(f1, f2));
    } catch (NoSuchMethodException | SecurityException |
        InstantiationException | IllegalAccessException
        | IllegalArgumentException | InvocationTargetException e) {
      e.printStackTrace();
    }
  }
}
```

##フィールド取得、設定
* publicフィールドは**getFieldメソッド**で取得
* その他フィールドは**getDeclaredFiledメソッド**で取得
* 引数は取得したいフィールドの名前を指定
* Fieldオブジェクト取得したら**get/setメソッド**でフィールド値を取得/設定
* privateフィールドはそのままアクセスできないので、**setAccessibleメソッド**でアクセスを明示的に許可
* 注意：リフレクションは**低速**

```java
//PersonクラスのlastNameフィールド取得

import java.lang.reflect.InvocationTargetException;
import example.Person;

public class ReflectField {

  public static void main(String[] args) {
    try {
      var clazz = Person.class;
      var con = clazz.getConstructor(String.class, String.class);
      var p = con.newInstance("太郎", "山田");
      //フィールド取得
      var last = clazz.getDeclaredField("lastName");
      //privateフィールドへのアクセスを明示的に許可
      last.setAccessible(true);
      //get/setメソッドでフィールド値取得/設定
      last.set(p, "鈴木");
      System.out.println(last.get(p)); //鈴木
    } catch (NoSuchMethodException | SecurityException |
        InstantiationException | IllegalAccessException
        | IllegalArgumentException | InvocationTargetException |
        NoSuchFieldException e) {
      e.printStackTrace();
    }
  }
}

```

```java
package example;
public class Person {
  private String firstName;
  private String lastName;
  public Person(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  @Override
  public String toString() {
    return String.format("名前は、%s %s です。",
        this.lastName, this.firstName);
  }
  public String getLastName() {
    return this.lastName;
  }
  public String getFirstName() {
    return this.firstName;
  }
}
```