公式ドキュメントに最初に導入されたアノテーションに関する一言:Javaアノテーションは、Javaコードのメタデータを提供するために使用されます。メタデータとして、アノテーションはコードの実行に直接影響しませんが、この目的で実際に使用できるアノテーションにはいくつかの種類があります。JavaアノテーションはJava5以降Javaに追加されています。この文章を読んだ後でも、混乱するかもしれません。次に、注釈の定義、メタ注釈、注釈属性、カスタム注釈、およびJDKが提供する注釈の注釈分析から注釈について再度学習します。
注釈の定義
- 新しいJavaクラスの日々の開発では、クラスとインターフェイスをさらに使用し、それらのようなアノテーションもクラスの一種です。使用される修飾子は@interfaceです。
注釈の書き方
- 新しいアノテーションMyTestAnnotationを作成します
public @interface MyTestAnnotation {
}
- 次に、作成したアノテーションをクラスまたはメソッドに適用できます
@MyTestAnnotation
public class test {
@MyTestAnnotation
public static void main(String[] args){
}
}
- 上記では、アノテーションの書き方を学びましたが、定義したアノテーションにコードを記述していません。このアノテーションは無意味です。アノテーションを機能させる方法は?次に、メタアノテーションについて理解します。
メタアノテーション
- メタ注釈は、その名前が示すように、注釈の注釈として理解できます。これは、注釈として機能し、注釈を使用して目的の機能を実現するのを容易にします。メタアノテーションには、@ Retention、@ Target、@ Document、@ AliExpress、および@Repeatable(JDK1.8で追加)の5種類があります。
@保持
- 保持の英語の意味には、保持と保持の意味があります。これは、アノテーションの存在ステージがソースコード(コンパイル期間)、バイトコード(クラスのロード)、またはランタイム(JVMで実行)で予約されていることを意味します。@Retentionアノテーションの列挙型RetentionPolicyを使用して、アノテーションの保持期間を示します
- @Retention(RetentionPolicy.SOURCE)、アノテーションはソースコードにのみ存在し、クラスバイトコードファイルには含まれていません
- @Retention(RetentionPolicy.CLASS)、デフォルトの保持ポリシー、アノテーションはクラスバイトコードファイルに存在しますが、実行時に取得することはできません
- @Retention(RetentionPolicy.RUNTIME)、アノテーションはクラスバイトコードファイルに存在し、実行時にリフレクションを通じて取得できます
- カスタムアノテーションの場合、前の分析では、カスタムアノテーションはソースコードまたはバイトコードファイルにのみ保存されていると機能せず、実行時に目的を達成するためにアノテーションを取得できるため、カスタムアノテーション@Retention( RetentionPolicy.RUNTIME)はで使用する必要があります
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTestAnnotation {
}
@目標
- ターゲットとは英語のターゲットを意味し、これも非常に理解しやすいです。@ Targetメタアノテーションを使用してアノテーションのスコープを表現することは、より具体的です。クラス、メソッド、メソッドパラメーター変数などにすることができます。列挙要素で表されますTypeTypesof
- @Target(ElementType.TYPE)関数インターフェイス、クラス、列挙、アノテーション
- @Target(ElementType.FIELD)属性フィールドと列挙定数の関数
- @Target(ElementType.METHOD)関数メソッド
- @Target(ElementType.PARAMETER)関数メソッドパラメーター
- @Target(ElementType.CONSTRUCTOR)関数コンストラクター
- @Target(ElementType.LOCAL_VARIABLE)はローカル変数に作用します
- @Target(ElementType.ANNOTATION_TYPE)はアノテーションに作用します(@Retentionアノテーションはこの属性を使用します)
- @Target(ElementType.PACKAGE)はパッケージに作用します
- @Target(ElementType.TYPE_PARAMETER)は、ジェネリック型、つまりジェネリックメソッド、ジェネリッククラス、ジェネリックインターフェイス(jdk1.8を追加)に作用します。
- @Target(ElementType.TYPE_USE)型の使用。クラス以外の任意の型に注釈を付けるために使用できます(jdk1.8が追加されました)
- 一般的に、より一般的に使用されるのはElementType.TYPEタイプです。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
@Documented
- 英語の文書は文書を意味します。その機能は、注釈の要素をJavadocに含めることができるようにすることです。
@遺伝性の
- 継承とは英語での継承を意味しますが、この継承は私たちが通常理解しているものと似ています。@合計で注釈が付けられたアノテーションは、親クラスを変更します。そのサブクラスが他のアノテーションによって変更されていない場合、そのサブクラスも親クラスのアノテーションを継承します。
- @AliExpressアノテーションの例を見てみましょう
/**自定义注解*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
/**父类标注自定义注解*/
@MyTestAnnotation
public class Father {
}
/**子类*/
public class Son extends Father {
}
/**测试子类获取父类自定义注解*/
public class test {
public static void main(String[] args){
//获取Son的class对象
Class<Son> sonClass = Son.class;
// 获取Son类上的注解MyTestAnnotation可以执行成功
MyTestAnnotation annotation = sonClass.getAnnotation(MyTestAnnotation.class);
}
}
@Repeatable
- 英語で繰り返し可能とは、繰り返し可能であることを意味します。名前が示すように、このメタアノテーションによって変更されたアノテーションは、オブジェクトに対して同時に複数回作用する可能性がありますが、アノテーションが作用するたびに異なる意味を表す可能性があります。
- ゲームをしている人の例を見てみましょう
/**一个人喜欢玩游戏,他喜欢玩英雄联盟,绝地求生,极品飞车,尘埃4等,则我们需要定义一个人的注解,他属性代表喜欢玩游戏集合,一个游戏注解,游戏属性代表游戏名称*/
/**玩家注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface People {
Game[] value() ;
}
/**游戏注解*/
@Repeatable(People.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Game {
String value() default "";
}
/**玩游戏类*/
@Game(value = "LOL")
@Game(value = "PUBG")
@Game(value = "NFS")
@Game(value = "Dirt4")
public class PlayGame {
}
- 上記の例を通して、ゲームコメントの括弧内の変数は何であるかという質問があるかもしれません。実際、これはゲームコメントで定義された属性に対応しています。次に、注釈の属性について学習し続けます。
注釈付き属性
- 前のセクションの@Repeatableアノテーションの例を通じて、アノテーションのプロパティについて説明しました。アノテーションの属性は、実際にはクラスで定義された変数と似ていますが、アノテーション内の変数はすべてメンバー変数(属性)であり、アノテーション内にメソッドはなく、メンバー変数のみであり、変数名は注釈括弧内の対応するパラメーター名前、括弧内の変数戻り値注釈対応するパラメータータイプ。これにより、上記の例をより深く理解できると思います。@Repeatableアノテーションの変数タイプは、アノテーション(インターフェース)に対応する汎用クラスです。
/**注解Repeatable源码*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
アノテーションの本質
- アノテーションの本質はアノテーションインターフェースです
/**Annotation接口源码*/
public interface Annotation {
boolean equals(Object obj);
int hashCode();
Class<? extends Annotation> annotationType();
}
- 上記のソースコードから、アノテーション自体がアノテーションインターフェイスのサブインターフェイスであることがわかります。つまり、アノテーションは実際には属性とメソッドを持つことができますが、インターフェイスの属性は静的なfinalであり、意味がありません。アノテーションの場合、インターフェイスを定義します。メソッドはアノテーションの属性と同等です。これは、アノテーションに属性メンバー変数しかない理由に対応します。実際、これはインターフェイスのメソッドであるため、メンバー変数には括弧があります。これは、注釈メンバー変数割り当ての括弧内に指定できるインターフェースとは異なります。
注釈属性タイプ
- アノテーション属性タイプは、以下にリストされたタイプを持つことができます
- 1.基本的なデータ型
- 2.ストリング
- 3.列挙型
- 4.注釈タイプ
- 5.クラスタイプ
- 6.上記のタイプの1次元配列タイプ
メンバー変数の割り当てに注釈を付ける
- 注釈に複数の属性がある場合、次の例のように、注釈の括弧内に「、」を使用して対応する属性を区切り、値を割り当てることができます。注釈は親クラスの属性を割り当てます。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
String name() default "mao";
int age() default 18;
}
@MyTestAnnotation(name = "father",age = 50)
public class Father {
}
注釈属性を取得する
- 先ほど、たくさんのアノテーションを定義する方法と配置する場所についてお話しましたが、今度はアノテーション属性の抽出を学び始めることができます。これがアノテーションを使用するための鍵です。属性の値を取得することがアノテーションを使用する目的です。
- アノテーション属性を取得する場合、もちろんそれはリフレクションですが、3つの基本的な方法があります
/**是否存在对应 Annotation 对象*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return GenericDeclaration.super.isAnnotationPresent(annotationClass);
}
/**获取 Annotation 对象*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/**获取所有 Annotation 对象数组*/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
前の例を組み合わせて、アノテーション属性を取得しましょう。アノテーションを取得する前に、メタアノテーション@Retention(RetentionPolicy.RUNTIME)を使用する必要があります。
public class test {
public static void main(String[] args) throws NoSuchMethodException {
/**
* 获取类注解属性
*/
Class<Father> fatherClass = Father.class;
boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
if(annotationPresent){
MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.age());
}
/**
* 获取方法注解属性
*/
try {
Field age = fatherClass.getDeclaredField("age");
boolean annotationPresent1 = age.isAnnotationPresent(Age.class);
if(annotationPresent1){
Age annotation = age.getAnnotation(Age.class);
System.out.println(annotation.value());
}
Method play = PlayGame.class.getDeclaredMethod("play");
if (play!=null){
People annotation2 = play.getAnnotation(People.class);
Game[] value = annotation2.value();
for (Game game : value) {
System.out.println(game.value());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
演算結果:
JDKが提供する注釈
注釈 | 効果 | 予防 |
---|---|---|
@オーバーライド | 現在のメソッドがオーバーライドされたメソッドであり、コンパイルフェーズ中にメソッドがチェックされることを説明するために使用されます | jdk1.5では、継承での書き換えのみを記述でき、jdk1.6では、インターフェース実装の書き換えとクラスの継承の書き換えのみを記述できます。 |
@非推奨 | 現在の方法が古い方法であることを説明するために使用されます | 番号 |
@SuppressWarnings | プログラムの警告を削除します。 | 番号 |
注釈とアプリケーション
- さて、冒頭の公式文書の説明をもう一度振り返ります
Javaアノテーションは、Javaコードのメタデータを提供するために使用されます。メタデータとして、アノテーションはコードの実行に直接影響しませんが、この目的で実際に使用できるアノテーションにはいくつかの種類があります。
- 以前の理解の後、アノテーションは実際には非常に便利なものです。生きる時が来て、アクションの領域を簡単に設定できます。アノテーションを何に使用するかが問題になります。
パラメータ設定に注釈を使用する
- 銀行振込の例を見てみましょう。銀行に振込サービスがあるとすると、為替レートの変化に応じて振込限度額が変わる場合があります。ビジネスコードを毎回変更するのではなく、アノテーションを使って振込限度額を柔軟に設定できます。
/**定义限额注解*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankTransferMoney {
double maxMoney() default 10000;
}
/**转账处理业务类*/
public class BankService {
/**
* @param money 转账金额
*/
@BankTransferMoney(maxMoney = 15000)
public static void TransferMoney(double money){
System.out.println(processAnnotationMoney(money));
}
private static String processAnnotationMoney(double money) {
try {
Method transferMoney = BankService.class.getDeclaredMethod("TransferMoney",double.class);
boolean annotationPresent = transferMoney.isAnnotationPresent(BankTransferMoney.class);
if(annotationPresent){
BankTransferMoney annotation = transferMoney.getAnnotation(BankTransferMoney.class);
double l = annotation.maxMoney();
if(money>l){
return "转账金额大于限额,转账失败";
}else {
return"转账金额为:"+money+",转账成功";
}
}
} catch ( NoSuchMethodException e) {
e.printStackTrace();
}
return "转账处理失败";
}
public static void main(String[] args){
TransferMoney(10000);
}
}
演算結果:
- 上記の例では、為替レートが変更されている限り、アノテーションの構成値を変更することで、現在の上限を直接変更できます。
サードパーティフレームワークの適用
- Android開発者として、私たちが通常使用するサードパーティのフレームワークButterKnife、Retrofit2、Dagger2などには注釈付きのアプリケーションがあります。これらのフレームワークの原則を理解するには、注釈の基本的な知識が不可欠です。
注釈の役割
- コンパイラーに情報を提供する:コンパイラーは、注釈を使用してエラーまたは警告を検出し、ログを出力できます。
- コンパイル段階での処理:ソフトウェアツールを使用して、注釈情報を使用してコード、ドキュメント、またはその他の対応する自動処理を自動的に生成できます。
- 実行時処理:一部の注釈は、プログラムの実行時にコード抽出を受け入れ、対応する操作を自動的に実行できます。
- 公式ドキュメントに記載されているように、アノテーションはメタデータを提供できます。転送の例では、アノテーション値を取得するプロセスは、開発者によって直接記述されたアノテーション抽出ロジックです。アノテーションの抽出と処理を処理するためのコードは、まとめてAPT(注釈処理ツール)。)。上記の転送例のprocessAnnotationMoneyメソッドは、APTツールクラスとして理解できます。