banner
jzman

jzman

Coding、思考、自觉。
github

Javaシリーズのアノテーション

Java 注釈(Annotation)は、Java マークアップ、メタデータとも呼ばれ、Java 1.5 以降に追加された特別な構文です。注釈を使用すると、Java のクラス、メソッド、プロパティ、パラメータ、パッケージなどにマークを付けることができ、リフレクションの原理を通じてこれらのメタデータにアクセスできます。注釈の使用はプログラムの正常な実行に影響を与えず、コンパイラの警告などの補助ツールにのみ影響を与えます。主な内容は以下の通りです:

  1. 注釈の機能
  2. 注釈の定義
  3. 組み込み注釈
  4. メタ注釈
  5. タイプ注釈

注釈の機能#

  • コンパイラは注釈を使用してエラーを検出し、警告を解除できます;
  • 注釈を使用すると、特定のコードを生成できます。たとえば、ButterKnife は注釈を使用して findViewById を簡素化します;
  • 特定の注釈は、実行時にチェックおよび操作できます。

注釈の定義#

注釈の定義は @interface をキーワードとして使用し、実際には java.lang.annotation.Annotation インターフェースを自動的に継承していることを示します。定義形式は以下の通りです:

@メタ注釈
public @interface AnnotationName{
    //設定パラメータ(パラメータタイプ パラメータ名())
    String name() default "hello";
}

設定パラメータの中のタイプには基本タイプ、Stringclass、列挙型および関連タイプの配列が含まれ、default を使用して設定パラメータのデフォルト値を設定できます。具体的な注釈の定義は以下の通りです:

@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
	String[] name() default "test";
}

組み込み注釈#

  1. @Override
  2. @Deprecated
  3. @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:未チェックの変換を実行したときの警告
  • fallthroughswitch プログラムブロックが次のケースに直接進むが break がないときの警告
  • path:クラスパス、ソースファイルパスなどに存在しないパスがあるときの警告
  • serial:シリアライズ可能なクラスに serialVersionUID 定義が欠けているときの警告
  • finally :任意の finally 節が正常に完了しないときの警告
  • all:上記のすべての状況に関する警告

以下は具体的なケースです:

public void test() {
	long date = Date.parse("2018-04-22");
}

上記のコードは、eclipse などの他の IDE を使用すると、2 つの警告が表示されます。1 つは古くなった API を使用していること、もう 1 つは変数 date が代入された後に使用されていないことです。警告のスクリーンショットは以下の通りです:

image

もちろん、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 の使用効果を示すスクリーンショットは以下の通りです:

image

特定の警告のみを解除したい場合は、以下のように記述できます:

//第一の方法
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 などが使用されており、これらは他の注釈に注釈を付けるための特別な注釈であり、これをメタ注釈と呼びます。具体的には以下を参照してください。

メタ注釈#

  1. @Target
  2. @Retention
  3. @Documented
  4. @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 にこの注釈を使用すると、その注釈が対応する任意の場所で使用できることを示します。たとえば、@TargetTYPE_PARAMETER を指定すると、カスタムタイプの宣言時にその注釈を使用でき、@Target で TYPE_USE を指定すると、任意のタイプの前にそのクラスを追加できます。これは、Java 開発者がタイプ注釈および関連プラグイン(Checker Framework)を使用して、コンパイル時に実行時の例外をチェックするのを便利にするためです。

以下に、TYPE_PARAMETERTYPE_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{}	
}

実際、注釈の構文は非常にシンプルで、注釈を定義すること自体は実際の開発には役立ちません。注釈は実行時にリフレクションを通じて注釈情報を取得することが最も重要です。注釈とリフレクションに関連する内容は、今後の投稿で学ぶ予定です。これで注釈に関する理解は終了です。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。