Javaの道(19)--注釈(構文、事前定義注釈、メタ注釈、繰返し注釈、注釈と反射)
10754 ワード
前言
公式注釈の定義は次のとおりです.
注釈(メタデータ形式)は、プログラム自体に属さないプログラムに関するデータを提供します.注釈は、それらの注釈のコードの操作に直接影響しません.
注記には、次のような多くの用途があります.コンパイラの情報-コンパイラは、注釈を使用してエラーを検出したり、警告を抑制したりすることができます. コンパイル時および導入時処理-ソフトウェアツールは、注釈情報を処理してコード、XMLファイルなどを生成することができる. ランタイム処理-実行時に注釈を確認できます.
初めて見た時に「このニマは何か」と思ったので、簡単な言い方に変えました.
注釈を「ラベル」と見なすことができますこの「ラベル」には、属性の評価(注釈要素)が含まれています.プログラム要素にこの「ラベル」(注釈を使用)を貼ることができます.貼ることができる以上、自然に何らかの方法(反射)でこの「ラベル」を引き裂くことができます(注釈を取得します).
このような比喩があれば、以下の注釈の説明がもっと理解しやすいと信じています.注解が何なのか理解できないと思ったら、「注解≒ラベル」を思い出すといいでしょう.
1.基礎知識
1.1注記のフォーマット注記に要素がない: 注記には複数の要素があり、これらの要素には値があります: 注記に1つの要素しかない場合: 同じ宣言に複数の注記を追加できます: は、反復注釈(同じ種類の注釈を用いる) を用いることができる.
1.2注記を使用できる場所
注記は、次の宣言に適用できます.クラス フィールド 方法 その他のプログラム要素 Java SE 8以前は、注釈は宣言にのみ適用されていました.Java SE 8からタイプ注記が追加されました.具体例は以下の通りです.クラスインスタンス を作成型変換 implements句 異常放出文 2.注釈タイプの宣言
注記タイプ定義は、
前述したように、注釈には次のように定義された要素があります.
このように定義すると、次の例のように、このタイプの注釈を使用して値を入力できます.
@MyAnnotation注記を定義するときに、デフォルト値を指定するためのキーワード
注記要素のタイプには制限があります.注記要素は次のタイプしか使用できません.基本データ型 String Class enum Annotation 以上のタイプの配列 3.事前定義された注記
3.1 Javaで使用される事前定義された注記
Javaで使用される注釈のタイプは次のとおりです.@Deprecatedは、注記された要素が破棄されたことを示し、これ以上使用すべきではありません.プログラムが@Deprecated注記付きのメソッド/クラス/フィールドを使用する限り、コンパイラは警告を生成します.使用を推奨しない場合は、Javadoc@deprecatedを使用してタグを付けます. @Overrideこの注釈通知コンパイラが注釈された要素は、スーパークラスで宣言された要素を上書きします.具体的には、書き換え方法の場合に使用されます.
実際には、書き換え方法に@Overrideは必須ではありません.この注釈を使用するのは、エラーを防ぐためです.
@Override注記を使用したメソッドがスーパークラスメソッドを正しく上書きしていない場合、コンパイラはエラーを報告します.
@SuppressWarningsこの注記は、コンパイラが生成可能な特定の警告を禁止することを通知します.たとえば、次の例では、通常コンパイラが警告を生成する有効な方法を使用しますが、@SuppressWarnings注釈を使用すると、警告の生成が禁止されます.
コンパイラ警告について、Java言語仕様では、
@SafeVarargsは、注釈がメソッドまたはコンストラクション関数に適用されると、可変パラメータに対して安全でない動作を実行しないことを保証します.この注釈を使用すると、可変パラメータに関するunchecked警告の生成が禁止されます. @FunctionalInterfaceはJava SE 8に導入され、タイプ宣言の目的は関数インタフェース注釈として指摘されている.
3.2元注記
他の注釈に適用される注釈をメタ注釈と呼ぶ.@Retentionコメントの格納方法を指定します.
@Documentedの役割は、注釈の要素をJavadocに含めることができる です.@Targetは、注釈を適用できるJava要素のタイプを制限します.
@Inheritedは、注釈タイプがスーパークラスから継承できることを示しています.スーパークラスが「@Inheritedで注記された」注釈を使用すると、そのサブクラスに注釈が追加されていない場合、サブクラスはスーパークラスの注釈を継承します.比較して、例を挙げると: @Repeatable Java SE 8に加えられた特性は,注釈された注釈が同じ宣言に複数回適用できることを示している.詳細については、「注記の繰り返し」セクションを参照してください.
4.繰り返し注記
Java SE 8には、繰り返し注釈が加えられており、宣言やタイプ参照に同じ注釈を適用することができます.
宣言には2つのステップがあります.繰り返し可能な注釈タイプ を宣言宣言に含まれる注釈タイプ 次の例では、重複注釈の宣言と使用方法を説明します.
ステップ1:繰り返し可能な注釈タイプを宣言する
注記タイプには@Repeatableメタ注記を使用してマークする必要があります.
ここで,@Repeatableメタ注釈の値は,Javaコンパイラが繰り返し注釈を格納するために生成したコンテナ注釈のタイプである.この例では、注記タイプがSchedulesであるため、@Schedule注記は@Schedules注記に格納されます.
ステップ2:注釈の種類を宣言
含まれる注記タイプには、配列タイプのvalue要素が必要です.配列のタイプには、繰り返し可能な注記タイプが必要です.
5.検索(Retrieving)注記-注記と反射
前に説明したのは、注釈をどのように宣言し、使用するかです.では、ここでは、注釈を取得する方法について説明します.
これは主に反射によって実現され、主に以下のAnnotatedElementインタフェースが提供する抽象的な方法に関する.注釈が適用されたかどうかを問い合わせる 注釈の取得
方法
機能
説明
プログラム要素に存在する指定したタイプの注釈を返し、そのタイプの注釈が存在しない場合nullを返します.
汎用パラメータは、注釈タイプまたは注釈タイプのサブクラスしか言えないことを示します.
継承された注釈を含む要素に存在するすべての注釈を返します.注記がない場合は、長さがゼロの配列を返します.
Java SE 8は、プログラム要素を修飾する、指定されたタイプの複数の注釈を得るための新しい方法を提供する.
プログラム要素を直接修飾し、指定したタイプの注記を返します.このタイプの注釈が存在しない場合はnullを返します.
継承された注記を無視
Java SE 8は、プログラム要素を直接修飾する、指定されたタイプの複数の注釈を得るための新しい方法を追加した.
継承された注記を無視
この要素に直接存在するすべての注記を返します.注記がない場合は、長さがゼロの配列を返します.
継承された注記を無視
具体的に例を示します.
MyAnnotation.java
TestAnnotation.java
出力結果
まとめ
注記はJavaが導入した非常に人気のあるメカニズムであり、構造化され、タイプチェック能力を持つ新しい方法を提供します.コードが乱雑で読みにくいことを招くことなく、コードにメタデータを加えることができます.
同時に、注釈はAndroidの多くのオープンソースフレームワークの実現の基礎でもある.例えばButterKnife、Retrofit、Dagger 2など.
注釈はやはりよく勉強する必要がある.
共に励ます.
公式注釈の定義は次のとおりです.
注釈(メタデータ形式)は、プログラム自体に属さないプログラムに関するデータを提供します.注釈は、それらの注釈のコードの操作に直接影響しません.
注記には、次のような多くの用途があります.
初めて見た時に「このニマは何か」と思ったので、簡単な言い方に変えました.
注釈を「ラベル」と見なすことができますこの「ラベル」には、属性の評価(注釈要素)が含まれています.プログラム要素にこの「ラベル」(注釈を使用)を貼ることができます.貼ることができる以上、自然に何らかの方法(反射)でこの「ラベル」を引き裂くことができます(注釈を取得します).
このような比喩があれば、以下の注釈の説明がもっと理解しやすいと信じています.注解が何なのか理解できないと思ったら、「注解≒ラベル」を思い出すといいでしょう.
1.基礎知識
1.1注記のフォーマット
@Entity
// , @Override
@Override
void myMethod(){...}
// @Author
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }
//@SuppressWarnings Java
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }
// value
@SuppressWarnings("unchecked")
void myMethod() { ... }
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }
1.2注記を使用できる場所
注記は、次の宣言に適用できます.
new @Interned MyObject();
myString = (@NonNull String) str;
class UnmodifiableList implements @Readonly List { ... }
void monitorTemperature() throws @Critical TemperatureException { ... }
注記タイプ定義は、
@interface
キーワードを使用して定義されるインタフェース定義と同様です.@interface MyAnnotation{
}
前述したように、注釈には次のように定義された要素があります.
@interface MyAnnotation{
int id() default 0;
String msg();
//...
}
このように定義すると、次の例のように、このタイプの注釈を使用して値を入力できます.
@MyAnnotation(id = 3, msg = "hello annotation")
public class Test {
//...
}
@MyAnnotation注記を定義するときに、デフォルト値を指定するためのキーワード
default
を使用しました.デフォルト値を指定すると、注釈を使用して値を入力しない場合、Javaは自動的にデフォルト値に設定します.次のように呼び出すことができます.@MyAnnotation(msg = "hello annotation")
public class Test {
//...
}
注記要素のタイプには制限があります.注記要素は次のタイプしか使用できません.
3.1 Javaで使用される事前定義された注記
Javaで使用される注釈のタイプは次のとおりです.
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
//
@Override
int overriddenMethod() { }
実際には、書き換え方法に@Overrideは必須ではありません.この注釈を使用するのは、エラーを防ぐためです.
@Override注記を使用したメソッドがスーパークラスメソッドを正しく上書きしていない場合、コンパイラはエラーを報告します.
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}
コンパイラ警告について、Java言語仕様では、
deprecation
とunchecked
の2つのカテゴリが指定されています.deprecation
:廃棄方法を使用した場合のカテゴリunchecked
:汎用型が出現する前に記述されたレガシーコードと対話する場合に発生する可能性のある警告カテゴリ.3.2元注記
他の注釈に適用される注釈をメタ注釈と呼ぶ.
RetentionPolicy.SOURCE
-タグの注記はソースレベルにのみ保持され、コンパイラによって無視されます.RetentionPolicy.CLASS
-タグ付けされた注記はコンパイラによってコンパイラによって保持されますが、Java仮想マシン(JVM)は無視されます.RetentionPolicy.RUNTIME
-マークされた注記はJVMによって保持されるため、ランタイム環境で使用できます.ElementType.ANNOTATION_TYPE
は、注釈タイプに適用することができる.ElementType.CONSTRUCTOR
は、構造関数に適用することができる.ElementType.FIELD
は、フィールドまたは属性に適用することができる.ElementType.LOCAL_VARIABLE
は、局所変数に適用することができる.ElementType.METHOD
は、方法レベルの注釈に適用することができる.ElementType.PACKAGE
は、パケット宣言に適用することができる.ElementType.PARAMETER
は、方法のパラメータに適用することができる.ElementType.TYPE
は、クラスの任意の要素に適用することができる.@Inherited
@interface Test {}
@Test
public class A {}
public class B extends A {}
の上で、@Test注釈は継承することができて、Aは@Test注釈を使って、BはAを継承して、しかもいかなる注釈を追加していないで、そこでBも@Testという注釈を持っています.4.繰り返し注記
Java SE 8には、繰り返し注釈が加えられており、宣言やタイプ参照に同じ注釈を適用することができます.
宣言には2つのステップがあります.
ステップ1:繰り返し可能な注釈タイプを宣言する
注記タイプには@Repeatableメタ注記を使用してマークする必要があります.
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
ここで,@Repeatableメタ注釈の値は,Javaコンパイラが繰り返し注釈を格納するために生成したコンテナ注釈のタイプである.この例では、注記タイプがSchedulesであるため、@Schedule注記は@Schedules注記に格納されます.
ステップ2:注釈の種類を宣言
含まれる注記タイプには、配列タイプのvalue要素が必要です.配列のタイプには、繰り返し可能な注記タイプが必要です.
public @interface Schedules {
Schedule[] value();
}
5.検索(Retrieving)注記-注記と反射
前に説明したのは、注釈をどのように宣言し、使用するかです.では、ここでは、注釈を取得する方法について説明します.
これは主に反射によって実現され、主に以下のAnnotatedElementインタフェースが提供する抽象的な方法に関する.
default boolean isAnnotationPresent(Class extends Annotation> annotationClass)
方法
機能
説明
T getAnnotation(Class annotationClass)
プログラム要素に存在する指定したタイプの注釈を返し、そのタイプの注釈が存在しない場合nullを返します.
汎用パラメータは、注釈タイプまたは注釈タイプのサブクラスしか言えないことを示します.
Annotation[] getAnnotations()
継承された注釈を含む要素に存在するすべての注釈を返します.注記がない場合は、長さがゼロの配列を返します.
default T[] getAnnotationsByType(Class annotationClass)
Java SE 8は、プログラム要素を修飾する、指定されたタイプの複数の注釈を得るための新しい方法を提供する.
default T getDeclaredAnnotation(Class annotationClass)
プログラム要素を直接修飾し、指定したタイプの注記を返します.このタイプの注釈が存在しない場合はnullを返します.
継承された注記を無視
default T[] getDeclaredAnnotationsByType(Class annotationClass)
Java SE 8は、プログラム要素を直接修飾する、指定されたタイプの複数の注釈を得るための新しい方法を追加した.
継承された注記を無視
Annotation[] getDeclaredAnnotations()
この要素に直接存在するすべての注記を返します.注記がない場合は、長さがゼロの配列を返します.
継承された注記を無視
具体的に例を示します.
MyAnnotation.java
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)// JVM ,
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})// 、
public @interface MyAnnotation{
String type() default "ignore";
String[] hobby();
}
TestAnnotation.java
@MyAnnotation(type = "class",hobby = {"sleep","play"})
public class TestAnnotation {
@MyAnnotation(type = "Field",hobby = {"read"})
private String whdalive;
@MyAnnotation(type = "Field",hobby = {"piano"})
private String cy;
@MyAnnotation(type = "Method",hobby = {"guitar"})
public void method1(){
}
public static void main(String[] args) {
//
Class clz = TestAnnotation.class;
//
boolean clzHasAnno = clz.isAnnotationPresent(MyAnnotation.class);
// ,
if(clzHasAnno) {
MyAnnotation annotation = clz.getAnnotation(MyAnnotation.class);
String type = annotation.type();
String[] hobby = annotation.hobby();
System.out.println(clz.getName() + ", type = " + type + ", hobby = " + Arrays.asList(hobby).toString());
}
//
Field[] fields = clz.getDeclaredFields();
for(Field field : fields) {
boolean fieldHasAnno = field.isAnnotationPresent(MyAnnotation.class);
if (fieldHasAnno) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
String type = annotation.type();
String[] hobby = annotation.hobby();
System.out.println(field.getName() + ", type = " + type + ", hobby = " + Arrays.asList(hobby).toString());
}
}
//
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
boolean methodHasAnno = method.isAnnotationPresent(MyAnnotation.class);
if (methodHasAnno) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
String type = annotation.type();
String[] hobby = annotation.hobby();
System.out.println(method.getName() + ", type = " + type + ", hobby = " + Arrays.asList(hobby).toString());
}
}
}
}
出力結果
com.whdalive.reflection.TestAnnotation, type = class, hobby = [sleep, play]
whdalive, type = Field, hobby = [read]
cy, type = Field, hobby = [piano]
method1, type = Method, hobby = [guitar]
まとめ
注記はJavaが導入した非常に人気のあるメカニズムであり、構造化され、タイプチェック能力を持つ新しい方法を提供します.コードが乱雑で読みにくいことを招くことなく、コードにメタデータを加えることができます.
同時に、注釈はAndroidの多くのオープンソースフレームワークの実現の基礎でもある.例えばButterKnife、Retrofit、Dagger 2など.
注釈はやはりよく勉強する必要がある.
共に励ます.