Java アノテーション

Java 11 Gold 取得に向けた学習記録

# アノテーションとは
Annotation = 注釈

メタデータとしてコードに付加される情報を**アノテーション**と言う。メタデータとは、情報自体に対する付随的な情報のことを指す。

```java:アノテーション
@アノテーション名(属性名=値)
```

## `@Deprecated` / `@SuppressWarnings`

特定の要素（クラス、メソッド、フィールド、コンストラクタなど）が**非推奨**であることを示すために使用されるアノテーションとして`java.lang.Deprecated`がある。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/Deprecated.html

また`@Deprecated`アノテーションは、`forRemoval`と`since`と言う[属性](#パラメータ)を持つ。

| 属性 |  |
|:-:|:-|
| `bool forRemoval()` | 将来のリリースで削除される予定であることを示す |
| `String since()` | 非推奨となったバージョンを示す |

```java:@Deprecated
public class MyClass {
    @Deprecated(since = "1.5", forRemoval = true)
    public void oldMethod() {
        // 古い実装
    }

    public void newMethod() {
        // 新しい実装
    }
}
```

`@Deprecated`アノテーションが付与された要素を使用した場合、コンパイル時に警告が表示されるため開発時に使用を回避することができる。

![anotation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/1d515493-f518-4c7f-3734-34ca8910a58e.png)

一方で、警告内容を理解した上でなお使用しなければならない場合、「警告が表示されていることがわかっていて使用しているもの」と「意図せず警告が表示されているもの」の区別がつかなくならないように、特定の警告を非表示にしたい場面がある。

そのような場合、`@SuppressWarnings`アノテーションを使用することで警告を非表示にすることができる。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/SuppressWarnings.html

`@SuppressWarnings`アノテーションには`String[]`型の`value`属性（`value`と言う名前で定義された[属性](#パラメータ)）が定義されていて、非表示にする警告の種類を渡す必要がある。

| `value`属性の引数 |  |
|:-:|:-|
| `"deprecation"` | `@Deprecated`による警告に対して使用する |
| `"removal"` | `@Deprecated(forRemoval = true)`による警告に対して使用する |
| `"unchecked"` | 未チェックのキャストや未チェックのジェネリクス操作に関する警告に対して使用する |
| `"serial"` | `Serializable`インターフェースの実装クラスに対して、`serialVersionUID`フィールドが定義されていないことに関する警告に対して使用する |
| `"unused"` | 使用されていない変数やメソッドに関する警告に対して使用する |

```java
public class MyClass {
    @Deprecated()
    public void oldMethod() {
    }
}
```

```java:@SuppressWarnings
public class Main {
    @SuppressWarnings("deprecation")
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.oldMethod();
    }
}
```

# カスタムアノテーション

アノテーションは`class`、`interface`、`enum`と同様に**型の一種**であり、`@interface`を使用して定義することができる。

```java:アノテーションの定義
public @interface MyAnnotation {}
```

開発者が自前で定義したカスタムアノテーションは、暗黙的に`java.lang.annotation.Annotation`インターフェースを継承したことになる。具体的には、カスタムアノテーションはコンパイルされると`java.lang.annotation.Annotation`インターフェースを継承したインターフェースに置き換わる。

暗黙的な継承は、`javap`コマンドによって確認することができる。

```terminal:暗黙的な継承
$ cd <MyAnnotationのディレクトリ>
$ javac MyAnnotation.java
$ javap MyAnnotation.class

Compiled from "MyAnnotation.java"
public interface MyAnnotation extends java.lang.annotation.Annotation {}
```

```java:暗黙的な継承
public interface MyAnnotation extends java.lang.annotation.Annotation {}
```

ただし`@MyAnnotation` を付与したクラス（アノテーションを付与したクラス）が、`MyAnnotation`を継承（`extends`）したり実装（`implements`）したクラスになるわけではない。

:::note info
アノテーションは型の一種。
:::

## パラメータ

### 定義
アノテーションには属性を定義することができ、この属性を**注釈パラメータ**と言う。

```java:定義
public @interface MyAnnotation {
    // 属性を定義
    String attribute();
}
```

パラメータに使用できる型は以下のみ。

- プリミティブ型
    - `int`、`long`、`short`、`float`、`double`、`byte`、`char`、`boolean`
- `String` 型
- `Class` 型
- Enum型
- アノテーション型
- 上記を要素にもつ配列

### 使い方

定義したパラメータ（`attribute`）には、次のような形で値（`"hello"`）を渡すことができる。

```java:使い方
@MyAnnotation(attribute = "hello")
public class MyClass {}
```

### 属性名の省略

- パラメータが一つ
- パラメータ名が`value`

の条件を満たす場合、アノテーション付与時にパラメータ名を省略することができる。

```java
public @interface MyAnnotation {
    String value();
}
```

```java:属性名の省略
@MyAnnotation("hello")
public class MyClass {}
```

### `()`の省略

パラメータがない場合、アノテーションの`()`は省略することができる。

パラメータを持たないアノテーションのように、付与されていること自体に意味を持つようなものを**マーカーアノテーション**と言う。

```java
public @interface MyAnnotation {}
```

```java:()の省略
@MyAnnotation
public class MyClass {
}
```

### デフォルト値

パラメータは、デフォルト値をあらかじめ定義しておくことができる。

```java:デフォルト値
public @interface MyAnnotation {
    String value() default "hello";
}
```

デフォルト値が定義されたアノテーションには、パラメータを渡す必要がない。

```java
// デフォルト値 "hello" が使用される
@MyAnnotation
public class MyClass {
}
```

### 配列のパラメータ

注釈パラメータが配列の場合、以下のような使い方になる。

```java:配列の注釈パラメータ
public @interface MyAnnotation {
    String[] array() ;
}
```

```java
@MyAnnotation(array = {"hello", "world", "!"})
public class MyClass {
}
```

このとき、もし渡す配列の要素が1つだけなら配列リテラル`{}`を省略することができる。

```java:配列リテラル {} の省略
@MyAnnotation(array = "hello")
public class MyClass {
}
```

## メタアノテーション

アノテーションを定義する際に利用するアノテーションを**メタアノテーション**と言う。メタアノテーションはアノテーションの構成情報を宣言するために使用される。

### `@Retention` / `RetentionPolicy`
Retention = 保持

**アノテーションの保持時間**を指定するために利用されるメタアノテーションとして、`java.lang.Retention`がある。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/annotation/Retention.html

メタアノテーションの`Retention`は、注釈パラメータ`value`にEnum型の`java.lnag.RetentionPolicy`を保持している。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/annotation/RetentionPolicy.html

| `RetentionPolicy` | 説明 |
|:-:|:-|
| `CLASS` | **デフォルト値**。`.class`ファイルには保持されるが、実行時にJVMによって保持されない。<br>`.class`ファイルに対して処理を行うような場合に使用する。<br>[リフレクション](#リフレクション)を利用して読み取ることができない。 |
| `RUNTIME` | `.class`ファイルと実行時のJVMで、どちらも保持される。<br>[リフレクション](#リフレクション)を利用して読み取ることができる。 |
| `SOURCE` | コンパイラによって破棄され、`.class`ファイルに保持されない（実行時も保持されない）。|

以下のような形で利用する。

```java:@Retention
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
```


### `@Target` / `ElementType`

**アノテーションの対象**を指定するために利用されるメタアノテーションとして、`java.lang.Target`がある。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/annotation/Target.html

`Retention`は、注釈パラメータ`value`にEnum型の`java.lnag.ElementType`を保持している。

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/lang/annotation/ElementType.html

| `ElementType` | 説明 |
|:-:|:-:|
|`ANNOTATION_TYPE`| アノテーションの定義（メタアノテーション） |
|`CONSTRUCTOR`| コンストラクタ |
|`FIELD`| フィールド |
|`LOCAL_VARIABLE`| ローカル変数 |
|`METHOD`| メソッド |
|`MODULE`| モジュール |
|`PACKAGE`| パッケージ |
|`PARAMETER`| パラメータ |
|`TYPE`| クラス、インターフェース、Enum |
|`TYPE_PARAMETER`| ジェネリクス（型引数） |
|`TYPE_USE`| 型に対してアノテーションを付与する |

以下のような形で利用する。

```java:@Target
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
public @interface MyAnnotation {}
```


## リフレクションからアノテーション情報を取得する

https://qiita.com/AsahinaKei/items/e5319b53e110e200e8dd

リフレクションを利用してアノテーション情報を取得するためには`Class<T>`クラスの`getAnnotation()`メソッドを使用する。

>`public <A extends Annotation> A getAnnotation​(Class<A> annotationClass)`

```java:アノテーション情報を取得する
Class<MyClass> myClassCls = MyClass.class;
Class<MyAnnotation> myAnnotationCls = MyAnnotation.class;
MyAnnotation myAnnotation = myClassCls.getAnnotation(myAnnotationCls);
```

# 参考
- [徹底攻略Java SE 11 Gold問題集[1Z0-816]対応](https://amzn.asia/d/0eV9L1Wp)
- [独習Java 新版](https://amzn.asia/d/0b7nxEwM)