注釈
注釈は、Javaソースコードのクラス、メソッド、フィールド、およびパラメータの前に配置される特別な種類の「コメント」です。
// this is a component:@Resource("hello")public class Hello {
@Inject
int n;
@PostConstruct
public void hello(@Param String name) {
System.out.println(name);
}
@Override
public String toString() {
return "Hello";
}
}
コメントはコンパイラによって直接無視され、コンパイラによってクラスファイルにパッケージ化できるため、コメントはラベルとして使用される一種の「メタデータ」です。
注釈の役割
JVMの観点からは、アノテーション自体はコードロジックに影響を与えません。アノテーションの使用方法は、ツールによって完全に決定されます。
Javaアノテーションは3つのカテゴリに分類できます。
最初のカテゴリは、コンパイラが使用するアノテーションです。次に例を示します。
- @Override:メソッドがオーバーライドを正しく実装しているかどうかをコンパイラーにチェックさせます。
- @SuppressWarnings:ここのコードによって生成された警告を無視するようコンパイラーに指示します。
このような注釈は.classファイルにコンパイルされず、コンパイル後にコンパイラーによって破棄されます。
2つ目のカテゴリは、ツールが.classファイルを処理するために使用するアノテーションです。たとえば、一部のツールは、クラスをロードしてクラスを動的に変更し、特別な関数を実装します。このような注釈は.classファイルにコンパイルされますが、ロード後はメモリに存在しません。このような注釈は、一部の低レベルライブラリでのみ使用されます。通常、これらの注釈を自分で処理する必要はありません。
3番目のタイプは、プログラムの実行時に読み取ることができる注釈で、ロード後に常にJVMに存在します。これは、最も一般的に使用される注釈でもあります。たとえば、@ PostConstructで構成されたメソッドは、コンストラクターを呼び出した後に自動的に呼び出されます(これは、注釈を読み取るJavaコードによって実装される関数であり、JVMは注釈を認識しません)。
注釈を定義するとき、構成パラメーターを定義することもできます。構成パラメーターには次のものがあります。 - すべての基本タイプ。
- ストリング;
- 列挙型
- 基本タイプの配列、文字列および列挙。
構成パラメーターは定数でなければならないため、上記の制限により、アノテーションの定義時に各パラメーターの値が既に決定されていることが保証されます。
注釈付きの構成パラメータにはデフォルト値を設定できます。構成パラメータがない場合は、デフォルト値が使用されます。
さらに、ほとんどのアノテーションにはvalueという構成パラメーターがありますが、このパラメーターに値を割り当てるには、定数を記述するだけでよく、これはvalueパラメーターを省略した場合と同じです。
コメントのみを記述する場合は、デフォルト値を使用することと同じです。
public class Hello {
@Check(min=0, max=100, value=55)
public int n;
@Check(value=99)
public int p;
@Check(99) // @Check(value=99)
public int x;
@Check
public int y;
}
@Checkは注釈です。最初の@Check(最小= 0、最大= 100、値= 55)は3つのパラメーターを明確に定義し、2番目の@Check(値= 99)は1つの値パラメーターのみを定義します。これは実際には@Check(99 )まったく同じです。最後の@Checkは、すべてのパラメーターがデフォルト値を使用することを意味します。
定義アノテーション
Java言語は@interface構文を使用して注釈を定義します。その形式は次のとおりです。
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
注釈付きパラメーターはパラメーターなしの方法に似ており、defaultを使用してデフォルト値を設定できます(強く推奨)。最も一般的に使用されるパラメータは、名前付きの値でなければなりません。
メタ注釈
他のアノテーションを変更できるアノテーションがいくつかあります。これらのアノテーションはメタアノテーションと呼ばれます。Java標準ライブラリはいくつかのメタ注釈を定義しています。メタ注釈を使用するだけでよく、通常はメタ注釈を記述する必要はありません。
@目標
最も一般的に使用されるメタ注釈は@Targetです。@Targetを使用して、Annotationをソースコードに適用できる場所を定義します。
- クラスまたはインターフェース:ElementType.TYPE;
- フィールド:ElementType.FIELD;
- メソッド:ElementType.METHOD;
- 構築メソッド:ElementType.CONSTRUCTOR;
- メソッドパラメータ:ElementType.PARAMETER。
たとえば、メソッドで@Reportを使用できるようにアノテーションを定義するには、@ Target(ElementType.METHOD)を追加する必要があります。
@Target(ElementType.METHOD)public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
定義アノテーション@Reportはメソッドまたはフィールドで使用でき、@ Targetアノテーションパラメータは配列{ElementType.METHOD、ElementType.FIELD}に変換できます。
@Target({
ElementType.METHOD,
ElementType.FIELD
})public @interface Report {
...
}
実際、@ Targetで定義される値はElementType []配列で、要素が1つだけの場合は、配列の書き込みを省略できます。
@保持
別の重要なメタアノテーション@Retentionは、アノテーションのライフサイクルを定義します。
- コンパイルのみ:RetentionPolicy.SOURCE;
- クラスファイルのみ:RetentionPolicy.CLASS;
- ランタイム:RetentionPolicy.RUNTIME。
@Retentionが存在しない場合、アノテーションはデフォルトでCLASSになります。カスタムアノテーションは通常RUNTIMEであるため、メタアノテーション@Retention(RetentionPolicy.RUNTIME)を追加する必要があります。
@Retention(RetentionPolicy.RUNTIME)public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
@遺伝性の
@Inheritedを使用して、サブクラスが親クラスによって定義された注釈を継承できるかどうかを定義します。@Inheritedは、@ Target(ElementType.TYPE)タイプの注釈に対してのみ有効であり、クラスの継承に対してのみ有効で、インターフェースの継承に対しては無効です。
@Inherited@Target(ElementType.TYPE)public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
使用中、クラスが@Reportを使用している場合:
@Report(type = 1)public class Person {
}
次に、そのサブクラスもこのアノテーションをデフォルトで定義します:
public class Student extends Person {
}
注釈を定義する方法
アノテーションを定義する手順を要約します。
最初の手順は@interfaceでアノテーションを定義することです:
public @interface Report {
}
2番目の手順はパラメータとデフォルト値を追加することです:
public @interface Report {
int type()default 0;
String level()default "Info";
String value()default "";
}
最も一般的に使用されるパラメーターはvalue()として定義されています。すべてのパラメーターにデフォルト値をできるだけ設定することをお勧めします。
3番目のステップは、メタアノテーションを使用してアノテーションを構成することです。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Report {
int type()default 0;
String level()default“ info”;
String value()default 「」;
}
これを設定し、一般的に読んで必要となる実行時にカスタムアノテーションのため@Target @リテンション、@リテンションランタイム一般的に、設定する必要があります。一般に、@ Inheritedおよび@Repeatableを記述する必要はありません。
アノテーションの処理
Javaアノテーション自体は、コードロジックに影響を与えません。@Retentionの構成によると:
- SOURCEタイプの注釈はコンパイル時に破棄されます。
- CLASSタイプの注釈はクラスファイルにのみ保存され、JVMにロードされません。
- タイプRUNTIMEの注釈はJVMにロードされ、実行時にプログラムによって読み取ることができます。
注釈の使用方法は、ツールによって完全に決定されます。SOURCEタイプの注釈は主にコンパイラーによって使用されるため、通常は使用されるだけであり、書き込まれることはありません。CLASSタイプの注釈は主に、クラスのロードを含む、基盤となるツールライブラリによって使用され、めったに使用されません。RUNTIMEタイプの注釈のみが使用されるだけでなく、多くの場合、記述する必要があります。
したがって、RUNTIMEタイプの注釈の読み方についてのみ説明します。
アノテーションも定義後のクラスであるため、すべてのアノテーションはjava.lang.annotation.Annotationから継承されるため、アノテーションを読み取るには、リフレクションAPIを使用する必要があります。
リフレクションAPIを使用してアノテーションを読み取るためにJavaによって提供されるメソッドには、次のものが含まれます。
特定のアノテーションがクラス、フィールド、メソッド、またはコンストラクターに存在するかどうかを判断します。 - Class.isAnnotationPresent(クラス)
- Field.isAnnotationPresent(クラス)
- Method.isAnnotationPresent(Class)
- Constructor.isAnnotationPresent(クラス)
は、リフレクションAPIを使用して注釈を読み取ります。 - Class.getAnnotation(Class)
- Field.getAnnotation(Class)
- Method.getAnnotation(Class)
- Constructor.getAnnotation(Class)
アノテーションの使用アノテーションの使用
方法は、プログラムによって完全に決定されます。たとえば、JUnitは@Testとマークされたすべてのメソッドを自動的に実行するテストフレームワークです。
@Rangeアノテーションを見て、それを使用して文字列フィールドのルールを定義します。フィールドの長さが@Rangeのパラメーター定義を満たしています。
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Range {
int min() default 0;
int max() default 255;
}
JavaBeanでは、この注釈を使用できます。
public class Person {
@Range(min=1, max=20)
public String name;
@Range(max=10)
public String city;
}
ただし、アノテーションは定義されており、プログラムのロジック自体には影響しません。注釈を使用するには、独自のコードを記述する必要があります。ここでは、Personインスタンスのチェックメソッドを記述します。これにより、Personインスタンスの文字列フィールドの長さが@Rangeの定義を満たすかどうかを確認できます。
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
// 遍历所有Field:
for (Field field : person.getClass().getFields()) {
// 获取Field定义的@Range:
Range range = field.getAnnotation(Range.class);
// 如果@Range存在:
if (range != null) {
// 获取Field的值:
Object value = field.get(person);
// 如果值是String:
if (value instanceof String) {
String s = (String) value;
// 判断值是否满足@Range的min/max:
if (s.length() < range.min() || s.length() > range.max()) {
throw new IllegalArgumentException("Invalid field: " + field.getName());
}
}
}
}
}
@Rangeアノテーションを通じて、check()メソッドを使用して、Personインスタンスの検査を完了することができます。検査ロジックは完全に自分で記述し、JVMは自動的に追加のロジックを注釈に追加しないことに注意してください。