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 つの警告が表示されます。一つは時代遅れの API を使用していること、もう一つは変数 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{}	
}

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

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