點(diǎn)贊關(guān)注塔次,不再迷路,你的支持對(duì)我意義重大有缆!
?? Hi象踊,我是丑丑。本文 「Java 路線」| 導(dǎo)讀 —— 他山之石棚壁,可以攻玉 已收錄通危,這里有 Android 進(jìn)階成長(zhǎng)路線筆記 & 博客,歡迎跟著彭丑丑一起成長(zhǎng)灌曙。(聯(lián)系方式在 GitHub)
前言
- 注解(Annotation)是 JDK 1.5 引進(jìn)的機(jī)制菊碟,允許將額外的元數(shù)據(jù)信息關(guān)聯(lián)到一個(gè)聲明上。
- 在這篇文章里在刺,我將帶你梳理
Java & Kotlin
注解的使用攻略逆害,追求簡(jiǎn)單易懂又不失深度,如果能幫上忙蚣驼,請(qǐng)務(wù)必點(diǎn)贊加關(guān)注魄幕!
目錄
1. 基本概念
-
問:什么是注解,為什么要使用注解颖杏?
答:注解(Annotation)是一種添加到聲明上的元數(shù)據(jù)(元數(shù)據(jù)是描述數(shù)據(jù)的數(shù)據(jù))纯陨。相對(duì)于無注解代碼,使用注解可以增強(qiáng)代碼可讀性留储、提供編譯時(shí)檢查翼抠、甚至可以結(jié)合注解處理器(APT)生成字節(jié)碼。例如:void func(@NonNull String str) { str.toString(); 這里依然可能拋出 NullPointerException } func(null); 編譯時(shí) Warning
這里使用
@NonNull
修飾了變量聲明获讳,表示參數(shù)str
應(yīng)接收一個(gè)非空值阴颖,否則編譯時(shí)會(huì)警告。需要注意的是丐膝,虛擬機(jī)不會(huì)阻止一個(gè)空值傳遞進(jìn)func()
量愧,方法內(nèi)部依然可能拋出NullPointerException
钾菊。如果你不理解,請(qǐng)看注解的本質(zhì): 問:說一下注解的本質(zhì)
答:注解本質(zhì)上是附加到聲明上的一種額外補(bǔ)充信息偎肃,在編譯后會(huì)被擦除煞烫,(有必要時(shí))生成 Class 文件常量池屬性,需要注意的是累颂,注解對(duì)它所修飾的代碼沒有直接影響红竭。具體來說,注解分為三種保留等級(jí)(Retention)喘落,見 2.4 節(jié)茵宪。
2. 注解的使用
2.1 聲明 & 應(yīng)用
在 Java 中,聲明注解需要用到@Interface
瘦棋,注解內(nèi)可以聲明注解參數(shù)稀火,用于在應(yīng)用注解時(shí)傳遞實(shí)參。例如:
public @interface MyAnnotation {
String value(); // 無默認(rèn)值
int num() default 1; // 默認(rèn)值 1
}
以下幾點(diǎn)需要注意下:
- 注解是不能包含方法的(因?yàn)樽⒔馐歉郊拥铰暶魃系难a(bǔ)充信息)
- 注解實(shí)參必須是編譯期已知的常量或類引用(因?yàn)榫幾g后注解被固化到 Class 文件的注解屬性中)
- 應(yīng)用注解時(shí)赌朋,每個(gè)注解參數(shù)都需要賦值凰狞,除非在聲明注解時(shí)已經(jīng)聲明了默認(rèn)值。另外沛慢,
value
命名的注解參數(shù)赡若,在傳值時(shí)可以省略參數(shù)名,這是一種簡(jiǎn)便寫法团甲,但是僅限給一個(gè)參數(shù)賦值的情況逾冬,例如:
以下三種語法效果相同:
@MyAnnotation("123") String str;
@MyAnnotation(value = "123") String str;
@MyAnnotation(value = "123",num = 1) String str;
在 Kotlin 中,注解的規(guī)則基本相同躺苦,語法上有所區(qū)別:
annotation class MyAnnotation(val value: String, val num: Int = 1)
2.2 元注解
元注解是用來修飾其他注解類型的注解身腻,元注解都需要使用@Target(ANNOTATION_TYPE)
修飾,JDK 內(nèi)置的元注解有:
2.3 注解應(yīng)用位置
使用@Target
來限制注解的應(yīng)用目標(biāo)匹厘,默認(rèn)為無限制嘀趟。當(dāng)存在多種應(yīng)用目標(biāo)時(shí),存在多個(gè)時(shí)使用{}
包裹多個(gè)目標(biāo)愈诚,例如:
@Target({FIELD, PARAMETER, LOCAL_VARIABLE})
public @interface MyAnnotation {
}
在 Java 中她按,注解應(yīng)用位置在 ElementType
枚舉中定義炕柔;在 Kotlin 中酌泰,注解應(yīng)用位置在AnnotationTarget
枚舉中定義汗唱,Kotlin 的注解應(yīng)用位置比 Java 更為豐富,具體如下:
另外哩罪,考慮到 Kotlin 代碼通常都擁有多重含義(例如一個(gè)可變屬性其實(shí)相當(dāng)于一個(gè)背域授霸、一個(gè) setter 和一個(gè) getter),為了更精準(zhǔn)控制注解位置碘耳,Kotlin 提供了精準(zhǔn)注解語法框弛,具體如下:
2.4 注解保留級(jí)別
使用@Retention
來聲明注解的保留等級(jí):
2.5 JDK 內(nèi)置注解
2.6 Kotlin 內(nèi)置注解
3. 注解的運(yùn)行時(shí)表示
前面我們提到辛辨,注解在編譯后擦除,如果注解的保留級(jí)別為 CLASS & RUNTIME
瑟枫,在 Class 文件中還會(huì)生成對(duì)應(yīng)的注解屬性斗搞,而RUNTIME
注解在類加載過程中會(huì)保留在 Class 對(duì)象中慷妙,因此可以通過反射獲取注解信息:
3.1 反射獲取運(yùn)行時(shí)注解信息
Java 將注解屬性包裝為Annotation
膝擂,所有注解都實(shí)現(xiàn)Annotation
接口:
public interface Annotation {
boolean equals(Object var1);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
每一種注解應(yīng)用目標(biāo)都提供了getAnnotation()
方法獲得修飾的注解。
Editting...
4. 注解的應(yīng)用場(chǎng)景
4.1 代替簡(jiǎn)單枚舉
枚舉的本質(zhì)是特殊的靜態(tài)成員變量架馋,相對(duì)于靜態(tài)常量,枚舉占用內(nèi)存更大叉寂。因此一些簡(jiǎn)單的枚舉應(yīng)該盡可能用注解代替,例如:
使用枚舉:
public enum Type {
START,END
}
Type type = Type.START;
---------------------------------------------------------
使用注解:
@Target({FIELD, PARAMETER, LOCAL_VARIABLE}) // 嚴(yán)謹(jǐn)起見
@Retention(RetentionPolicy.SOURCE)
@IntDef({Type.START, Type.END})
public @interface Type {
int START = 0;
int END = 1;
}
@Type int type = Type.START;
需要注意的是伊约,因?yàn)槭褂昧?code>SOURCE注解孕蝉,所以注解只會(huì)在編寫源碼時(shí)提供代碼提示,不會(huì)影響編譯過程超埋。
4.2 運(yùn)行時(shí)注解信息
在運(yùn)行時(shí),可以通過反射動(dòng)態(tài)獲得注解信息(限RUNTIME
注解)佳鳖,通常是做一些邏輯判斷,具體見第 3 節(jié)来庭。
4.3 注解處理器(APT)
注解處理是Java
編譯前端處理過程中穿挨,程序員可控性最強(qiáng)的一個(gè)階段肴盏,在很多開源框架中(Glide、EventBus帽衙、Tinker、ARouter)厉萝,都可以看到注解處理器的身影。在這篇文章里章母,我們?cè)敿?xì)討論:《Java | 注解處理器(APT)原理解析 & 實(shí)踐》翩剪,請(qǐng)關(guān)注!
4.4 字節(jié)碼增強(qiáng)
在編譯出 Class 文件后舞肆,可以通過修改 Class 文件內(nèi)容來修改程序邏輯博杖,舉個(gè)例子,我們可以定義一個(gè)應(yīng)用于點(diǎn)擊事件方法上的注解剃根,并利用注解參數(shù)用來限制點(diǎn)擊事件觸發(fā)頻率。在這篇文章里廉油,我們?cè)敿?xì)討論:《Android | 使用 AspectJ 限制按鈕快速點(diǎn)擊》苗傅,請(qǐng)關(guān)注!
5. 總結(jié)
注解是一種添加到聲明上的元數(shù)據(jù)渣慕,需要注意的是,注解在編譯后被擦除眨猎,CLASS & RUNTIME
注解會(huì)保留到 Class 文件的注解屬性中强经,而RUNTIME
注解在類加載后會(huì)保存在 Class 對(duì)象,可以反射獲取。
參考資料
- 《Kotlin實(shí)戰(zhàn)》 (第10章)—— [俄] Dmitry Jemerov信殊,Svetlana Isakova 著
- 《Kotlin 核心編程》(第8章) —— 水滴技術(shù)團(tuán)隊(duì) 著
- 《Java編程思想》 (第19逮矛、23章)—— [美] Bruce Eckel 著
- 《深入理解Java虛擬機(jī)(第3版)》(第6转砖、8、10章)—— 周志明 著
創(chuàng)作不易晋控,你的「三連」是丑丑最大的動(dòng)力姓赤,我們下次見雨让!