「Java 路線」| 注解(含 Kotlin)

點(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核心編程》

另外哩罪,考慮到 Kotlin 代碼通常都擁有多重含義(例如一個(gè)可變屬性其實(shí)相當(dāng)于一個(gè)背域授霸、一個(gè) setter 和一個(gè) getter),為了更精準(zhǔn)控制注解位置碘耳,Kotlin 提供了精準(zhǔn)注解語法框弛,具體如下:

引用自《Kotlin核心編程》

2.4 注解保留級(jí)別

使用@Retention來聲明注解的保留等級(jí):

三種注解保留等級(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)力姓赤,我們下次見雨让!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市脓鹃,隨后出現(xiàn)的幾起案子薪者,更是在濱河造成了極大的恐慌,老刑警劉巖毕谴,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件距芬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡舀武,警方通過查閱死者的電腦和手機(jī)离斩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纵朋,“玉大人茄袖,你說我怎么就攤上這事∠芟椋” “怎么了家乘?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵仁锯,是天一觀的道長(zhǎng)翔悠。 經(jīng)常有香客問我业崖,道長(zhǎng)蓄愁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任妇斤,我火速辦了婚禮丹拯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘死相。我一直安慰自己剑刑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布钮惠。 她就那樣靜靜地躺著七芭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狸驳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天撰糠,我揣著相機(jī)與錄音辩昆,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛砚尽,可吹牛的內(nèi)容都是我干的辉词。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼敷搪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼隘蝎!你這毒婦竟也來了襟企?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤顽悼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冰评,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體木羹,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年抛人,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脐瑰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绝页,死狀恐怖寂恬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初肉,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布井佑,位于F島的核電站,受9級(jí)特大地震影響躬翁,放射性物質(zhì)發(fā)生泄漏焦蘑。R本人自食惡果不足惜盒发,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一宁舰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛮艰,春花似錦、人聲如沸壤蚜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽著蟹。三九已至,卻和暖如春萧豆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背源内。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工份殿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卿嘲。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓拾枣,卻偏偏與公主長(zhǎng)得像盒让,于是被迫代替她去往敵國(guó)和親司蔬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子邑茄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353