Java 注釈(Annotation)は、Java マークアップ、メタデータとも呼ばれ、Java 1.5 以降に追加された特別な構文です。注釈を使用すると、Java のクラス、メソッド、プロパティ、パラメータ、パッケージなどにマークを付けることができ、リフレクションの原理を通じてこれらのメタデータにアクセスできます。注釈の使用はプログラムの正常な実行に影響を与えず、コンパイラの警告などの補助ツールにのみ影響を与えます。主な内容は以下の通りです:
- 注釈の機能
- 注釈の定義
- 組み込み注釈
- メタ注釈
- タイプ注釈
注釈の機能#
- コンパイラは注釈を使用してエラーを検出し、警告を解除できます;
- 注釈を使用すると、特定のコードを生成できます。たとえば、
ButterKnife
は注釈を使用してfindViewById
を簡素化します; - 特定の注釈は、実行時にチェックおよび操作できます。
注釈の定義#
注釈の定義は @interface
をキーワードとして使用し、実際には java.lang.annotation.Annotation
インターフェースを自動的に継承していることを示します。定義形式は以下の通りです:
@メタ注釈
public @interface AnnotationName{
//設定パラメータ(パラメータタイプ パラメータ名())
String name() default "hello";
}
設定パラメータの中のタイプには基本タイプ、String
、class
、列挙型および関連タイプの配列が含まれ、default
を使用して設定パラメータのデフォルト値を設定できます。具体的な注釈の定義は以下の通りです:
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
String[] name() default "test";
}
組み込み注釈#
- @Override
- @Deprecated
- @SuppressWarnings
以下は上記の 3 つの組み込み注釈の宣言です:
//現在のメソッドがスーパークラスのメソッドをオーバーライドすることを示し、コンパイル時に形式チェックを行います
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
//クラスまたはメソッドがもはや推奨されないことを示し、古くなったとマークしますが、使用は可能です
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
//不適切なコンパイラ警告メッセージを無効にすることを示します
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
上記の 3 つの注釈の宣言から、@SuppressWarnings
では配列が定義されており、この配列はこの注釈の具体的なターゲットを示します。SuppressWarnings
で使用できる値は、一般的に以下の通りです:
deprecation
:古くなったクラスまたはメソッドを使用したときの警告unused
:未使用の変数があるときの警告unchecked
:未チェックの変換を実行したときの警告fallthrough
:switch
プログラムブロックが次のケースに直接進むがbreak
がないときの警告- path:クラスパス、ソースファイルパスなどに存在しないパスがあるときの警告
- serial:シリアライズ可能なクラスに
serialVersionUID
定義が欠けているときの警告 - finally :任意の
finally
節が正常に完了しないときの警告 - all:上記のすべての状況に関する警告
以下は具体的なケースです:
public void test() {
long date = Date.parse("2018-04-22");
}
上記のコードは、eclipse などの他の IDE を使用すると、2 つの警告が表示されます。1 つは古くなった API を使用していること、もう 1 つは変数 date
が代入された後に使用されていないことです。警告のスクリーンショットは以下の通りです:
もちろん、IDE はこれらの警告を解除するために SuppressWarnings
を追加するかどうかを提示します。前述のように、注釈 @SuppressWarnings
の宣言には設定パラメータが必要で、このパラメータは配列であり、配列名は value
ですが、この名前は省略可能です。具体的には以下の通りです:
//省略しない
public void test2() {
@SuppressWarnings(value= {"deprecation", "unused"})
long date = Date.parse("2018-04-22");
}
//省略
public void test2() {
@SuppressWarnings({"deprecation", "unused"})
long date = Date.parse("2018-04-22");
}
@SuppressWarnings
の使用効果を示すスクリーンショットは以下の通りです:
特定の警告のみを解除したい場合は、以下のように記述できます:
//第一の方法
public void test2() {
@SuppressWarnings(value = {"deprecation"})
long date = Date.parse("2018-04-22");
System.out.println(date);
}
//第二の方法
public void test2() {
@SuppressWarnings({"deprecation"})
long date = Date.parse("2018-04-22");
System.out.println(date);
}
注意:注釈の設定パラメータ名が value の場合、注釈を設定する際に value を省略できます。逆に、他の名前を使用する場合は、最初の方法を使用して設定パラメータ名を指定する必要があります。
もちろん、他の注釈も @SuppressWarnings
と似ており、@Override
、@Deprecated
はその宣言から直接使用でき、具体的なターゲットを指定する必要はありません。その宣言注釈には @Documented
、@Retention
、@Target
などが使用されており、これらは他の注釈に注釈を付けるための特別な注釈であり、これをメタ注釈と呼びます。具体的には以下を参照してください。
メタ注釈#
- @Target
- @Retention
- @Documented
- @Inherited
@Target#
@Target
は注釈の使用範囲を説明するために使用され、その宣言は以下の通りです:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
@Target
の宣言から、@Target
注釈を使用するには具体的な Java メンバーを指定する必要があり、つまりその注釈がどの位置で使用されるかを示します。具体的には列挙型 ElementType
で定義されています。具体的には以下の通りです:
public enum ElementType {
TYPE, //クラス、インターフェース、注釈、列挙
FIELD, //プロパティ(列挙定数を含む)
METHOD, //メソッド
PARAMETER, //パラメータ
CONSTRUCTOR, //コンストラクタ
LOCAL_VARIABLE, //ローカル変数
ANNOTATION_TYPE,//注釈
PACKAGE, //パッケージ
/**
* タイプ注釈
* @since 1.8
*/
TYPE_PARAMETER,
TYPE_USE
}
@Retention#
@Retention は、どのレベルでその注釈の情報を保持するかを示します。その宣言は以下の通りです:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* 保持ポリシーを返します。
* @return 保持ポリシー
*/
RetentionPolicy value();
}
@Retention
の宣言から、@Retention
を使用する際には、保持する値(RetentionPolicy)を指定する必要があります。具体的な値は以下の通りです:
public enum RetentionPolicy {
SOURCE, //コンパイル時に破棄され、ソースコード内にのみ存在
CLASS, //デフォルトのポリシーで、実行時に破棄され、class ファイル内にのみ存在
RUNTIME //コンパイル時に注釈情報が class ファイルに記録され、実行時に保持され、リフレクションを通じて注釈情報を取得可能
}
@Documented と @Inherited はパラメータを持たないマーク注釈で、@Documented はその注釈をユーザードキュメントに表示することを示し、@Inherited はその注釈がクラスにのみ有効であり、サブクラスに継承されることを示します。
タイプ注釈#
メタ注釈の説明から、Java8 以降に新たにタイプ注釈が追加されたことがわかります。@Target
にこの注釈を使用すると、その注釈が対応する任意の場所で使用できることを示します。たとえば、@Target
で TYPE_PARAMETER
を指定すると、カスタムタイプの宣言時にその注釈を使用でき、@Target
で TYPE_USE を指定すると、任意のタイプの前にそのクラスを追加できます。これは、Java 開発者がタイプ注釈および関連プラグイン(Checker Framework)を使用して、コンパイル時に実行時の例外をチェックするのを便利にするためです。
以下に、TYPE_PARAMETER
と TYPE_USE
を指定した注釈をそれぞれ定義します。具体的には以下の通りです:
//1. TYPE_PARAMETER
@Target(value = {ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
String value();
}
//2. TYPE_USE
@Target(value = ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}
次に、以下のケースでこれらの 2 つの注釈を使用します。具体的には以下の通りです:
/**
* テスト注釈
* @author jzman
*/
public class TestAnnotation {
//...
/**
* ElementType.TYPE_PARAMETER
* カスタムタイプ宣言時に使用、注釈 @TypeParameterAnnotation
* @param <T>
*/
static class TypeAnnotationA<@TypeParameterAnnotation(value="hello") T>{
/**
* ElementType.TYPE_USE
* 任意のタイプの前に使用可能(TYPE_PARAMETER を含む)
*/
//インスタンスを作成
MyType myType = new @TypeUseAnnotation MyType();
//オブジェクトタイプ
Object obj = (@TypeUseAnnotation Object) myType;
//ジェネリック
ArrayList<@TypeUseAnnotation T> list = new ArrayList<>();
//パラメータ内のタイプ
public String testA(@TypeUseAnnotation String test) {
return "Hello"+test;
}
//列挙型
public void testB(@TypeUseAnnotation Color color) {
//...
}
enum Color{
RED, GREEN, BLUE
}
}
static class MyType{}
}
実際、注釈の構文は非常にシンプルで、注釈を定義すること自体は実際の開発には役立ちません。注釈は実行時にリフレクションを通じて注釈情報を取得することが最も重要です。注釈とリフレクションに関連する内容は、今後の投稿で学ぶ予定です。これで注釈に関する理解は終了です。