Kotlin注解(2)自定義注解

  • 聲明注解
  • 案例:使用元注解
  • 注解目標聲明
  • 案例:讀取運行時注解信息

??與基本注解不同漠畜,在一般的應(yīng)用開發(fā)中不會直接使用元注解欧漱,在開發(fā)框架或生成工具時會用到一些自定義注解爬范,元注解是自定義注解的組成要素财著。

一侧巨、聲明注解

??聲明自定義注解可以使用 annotation 關(guān)鍵字實現(xiàn)稠歉,最簡單形式的注解實例代碼如下:

annotation class Marker

??上述代碼聲明一個 Marker 注解射众,annotation 聲明一個注解類型澎胡,注解的可見性有 公有的钮追、內(nèi)部的私有的预厌,不能是保護的。

??Marker 注解中不包含任何的成員元媚,這種注解稱為標記注解轧叽,標記注解屬于基本注解。注解也可以有成員屬性刊棕,通過構(gòu)造函數(shù)初始化成員屬性炭晒。如下:

annotation class MyAnnotation1(val value: String)

??代碼中 (val value: String) 是聲明注解 MyAnnotation1 的構(gòu)造函數(shù),構(gòu)造函數(shù)參數(shù)類型只能是 基本數(shù)據(jù)類型甥角、字符串网严、類類型(KClass)枚舉嗤无、數(shù)組其他的注解類型震束。

??此外怜庸,構(gòu)造函數(shù) (val value: String) 同時聲明了注解 MyAnnotation1 有一個成員屬性 value,成員屬性的可見性只能是公有的垢村。

??注解成員屬性也可以有默認值割疾,如下:

annotation class MyAnnotation2(val value: String = "注解信息", val count: Int = 20)

??使用這些注解示例代碼如下:

@Marker     // 1
class Person {
    // 名字
    @MyAnnotation1(value = "名字")        // 2
    @JvmField
    var name = "Tony"

    // 年齡
    @MyAnnotation2(value = "年齡")        // 3
    var age = 18
}

@Marker     // 4
fun main(args: Array<String>) {
    @Marker     // 5
    @MyAnnotation2(value = "實例化Person", count = 1)      // 6
    val p = Person()
}

??默認情況下注解可以修飾任意的代碼元素(類、接口嘉栓、屬性杈曲、變量、函數(shù) 和 數(shù)據(jù)類型等)胸懈。代碼第1行担扑、第4行和第5行都使用 @Marker 注解,分別修飾 類趣钱、函數(shù) 和 變量涌献。

??代碼第2行是使用 @MyAnnotation1(value="名字") 注解修飾 name 成員屬性,其中 value = "名字" 是為 value 屬性提供數(shù)值首有。代碼第3行是使用 MyAnnotation2(value = "年齡") 注解修飾成員函數(shù)燕垃,@MyAnnotation2 有兩個成員屬性,但是只為 value 成員屬性賦值井联,另一個成員屬性 count 是使用默認值卜壕。代碼第6行使用 @MyAnnotation2(value = "實例化Person", count = 1) 注解修飾變量。

二烙常、案例:使用元注解

??上面聲明的注解只是最基本形式的注解轴捎,對于復(fù)雜的注解可以在聲明注解時使用元注解。下面在一個自定義注解中使用元注解蚕脏。在此案例中定義了兩個注解侦副。

  • 第一個注解,用來注解類和接口
@MustBeDocumented                                       // 1
@Target(AnnotationTarget.CLASS)                         // 2
@Retention(AnnotationRetention.RUNTIME)                 // 3
annotation class MyAnnotation(val description: String)  // 4

代碼第4行聲明注解類型 MyAnnotation驼鞭,其中使用了三個元注解修飾 MyAnnotaion 注解秦驯。代碼第1行使用 @MustBeDocumented 指定 MyAnnotaion 注解信息可以被文檔生成工具讀取。代碼第2行使用 @Target(AnnotationTarget.CLASS) 指定 MyAnnotaion 注解用于修飾類 和 接口等類型挣棕。代碼第3行 @Retention(AnnotationRetention.RUNTIME) 指定 MyAnnotaion 注解信息可以在運行時被讀取译隘。代碼第4行的 descriptionMyAnnotaion 注解的屬性。

  • 第二個注解洛心,用來注解類中成員屬性和函數(shù)
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)                 // 1
@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER
)                                                       // 2
annotation class MemberAnnotation(val type: KClass<*> = Unit::class, val description: String)   // 3

??上述代碼第3行是聲明注解類型 MemberAnnotation固耘,其中也使用三個元注解修飾 MemberAnnotation 注解。代碼第1行的 @Retention(AnnotationRetention.RUNTIME) 指定 MemberAnnotation 注解信息可以在運行時被讀取皂甘。代碼第2行 @Target 指定 MemberAnnotation 注解用于修飾類中成員(函數(shù)玻驻、屬性、屬性的getter訪問器 和 屬性的setter訪問器)偿枕。

??代碼第3行中還聲明 MemberAnnotation 注解的屬性是 typedescription璧瞬,type 類型是 KClass<*>,默認值是 Unit::class渐夸。description 類型是 String嗤锉,沒有設(shè)置默認值。

??提示:代碼第3行的 KClass<*> 類型表示 KClass 的泛型墓塌,* 是泛型通配符瘟忱,可以是任何類型。多數(shù)情況下泛型尖括號中指定的都是某個具體類型苫幢,泛型也是為此而設(shè)計的访诱。但是有時確實不需要知道具體類型,或者說什么類型都可以韩肝,此時可以使用 * 通配所有類型触菜。

  • 使用 MyAnnotationMemberAnnotation 注解的 Student
@MyAnnotation(description = "這是一個測試類")  // 1
class Student {
    @MemberAnnotation(type = String::class, description = "名字") // 2
    @get:MemberAnnotation(type = String::class, description = "獲得名字") // 3
    var name: String? = null
        private set

    @MemberAnnotation(type = Int::class, description = "年齡")    // 4
    @get:MemberAnnotation(type = Int::class, description = "獲得年齡") // 5
    var age: Int = 0
        private set

    @MemberAnnotation(description = "設(shè)置姓名和年齡")  // 6
    fun setNameAndAge(name: String, age: Int) {
        this.name = name
        this.age = age
    }

    override fun toString(): String {
        return "Person [name=$name, age=$age]"
    }
}

??如果當(dāng)前類與注解不在同一個包中,則需要將注解引入哀峻。代碼第1行的 @MyAnnotation 只能在 Student 類聲明之前涡相。代碼第2行 @MemberAnnotation 注解 name 成員屬性,代碼第3行 @get:MemberAnnotation 注解 name 成員屬性 getter 訪問器剩蟀。代碼第4行 @MemberAnnotation 注解 age 成員屬性催蝗。代碼第5行 @get:MemberAnnotation 注解 age 成員屬性 getter 訪問器。第6行是使用 @MemberAnnotation 注解成員函數(shù)育特。

三丙号、注解目標聲明

??上面案例代碼的第3行和第5行都用到 @get:MemberAnnotation 形式的注解,其中 get 是聲明 MemberAnnotation 注解應(yīng)用在 name 成員屬性 getter 訪問器上缰冤,事實上一個 name 成員屬性有很多可以被注解的元素槽袄,這些元素有:屬性本身getter訪問器锋谐、setter訪問器支持字段遍尺。

??Kotlin 中使用某個注解進行修飾時,當(dāng)多個元素都有被修飾的可能時涮拗,可以使用注解目標聲明指定注解修飾的具體元素乾戏,上面案例中的 get(代碼第3和第5行) 就是注解目標聲明。Kotlin 中的注解目標列表如下:

目標 說明
file 文件
property 屬性三热,使用此目標Java中不可見
field 字段
get getter訪問器
set setter訪問器
receiver 擴展函數(shù)或?qū)傩缘膮?shù)
param 構(gòu)造函數(shù)的參數(shù)
setparam setter訪問器參數(shù)
delegate 保存委托屬性的字段

??從表中可見使用的 @file:JvmName("名稱") 注解也使用了 file 目標聲明鼓择。

四、案例:讀取運行時注解信息

??注解時為工具讀取信息而準備的就漾。有些工具可以讀取源代碼文件中的注解信息呐能;有的可以讀取字節(jié)碼文件中的注解信息;有的可以在運行時讀取注解信息。但是讀取這些注解信息的代碼都是一樣的摆出,區(qū)別只在于自定義注解中 @Retention 的保留期不同朗徊。

??讀取注解信息所需要的反射 API 是 KClass 類的 findAnnotation 函數(shù)。讀取運行時 Student 類中注解信息代碼如下:

fun main(args: Array<String>) {
    val clz = Student::class        // 1

    // 讀取類注解
    val ann = clz.findAnnotation<MyAnnotation>()    // 2
    println("類${clz.simpleName}偎漫,注解描述:${ann?.description ?: "無"}")   // 3

    // 讀取成員函數(shù)的注解信息
    println("---------- 讀取成員函數(shù)的注解信息 ----------")
    clz.declaredFunctions.forEach {     // 4
        val ann = it.findAnnotation<MemberAnnotation>()     // 5
        println("函數(shù)${it.name}爷恳,注解描述:${ann?.description ?: "無"}")
    }

    // 讀取屬性的注解信息
    println("---------- 讀取屬性的注解信息 ----------")
    clz.declaredMemberProperties.forEach {      // 6
        val ann = it.findAnnotation<MemberAnnotation>()     // 7
        println("屬性${it.name},注解描述:${ann?.description ?: "無"}")
    }
}

運行結(jié)果:

類Student象踊,注解描述:這是一個測試類
---------- 讀取成員函數(shù)的注解信息 ----------
函數(shù)setNameAndAge温亲,注解描述:設(shè)置姓名和年齡
函數(shù)toString,注解描述:無
---------- 讀取屬性的注解信息 ----------
屬性age杯矩,注解描述:年齡
屬性name栈虚,注解描述:名字

Process finished with exit code 0

??上述代碼第1行是創(chuàng)建 Student 類引用,代碼第2行使用 findAnnotation 函數(shù)查找 Student 類是否有 MyAnnotation史隆。代碼第3行中 ann?.description 表達式讀取 MyAnnotation 注解中的 description 屬性內(nèi)容魂务。

??代碼第4行 clz.declaredFunctions 返回當(dāng)前 Student 類中所有成員函數(shù)的集合。代碼第5行是查找 Student 成員函數(shù)是否有 MemberAnnotation 注解逆害。

??代碼第6行 clz.declaredMemberProperties 返回當(dāng)前 Student 類中所有成員的屬性集合头镊。代碼第7行是查找 Student 成員屬性是否有 MemberAnnotation 注解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末魄幕,一起剝皮案震驚了整個濱河市相艇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纯陨,老刑警劉巖坛芽,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翼抠,居然都是意外死亡咙轩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門阴颖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來活喊,“玉大人,你說我怎么就攤上這事量愧〖鼐眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵偎肃,是天一觀的道長煞烫。 經(jīng)常有香客問我,道長累颂,這世上最難降的妖魔是什么滞详? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上料饥,老公的妹妹穿的比我還像新娘蒲犬。我一直安慰自己,他們只是感情好稀火,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布暖哨。 她就那樣靜靜地躺著赌朋,像睡著了一般凰狞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沛慢,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天赡若,我揣著相機與錄音,去河邊找鬼团甲。 笑死逾冬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躺苦。 我是一名探鬼主播身腻,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匹厘!你這毒婦竟也來了嘀趟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤愈诚,失蹤者是張志新(化名)和其女友劉穎她按,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炕柔,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡酌泰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匕累。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陵刹。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖欢嘿,靈堂內(nèi)的尸體忽然破棺而出衰琐,到底是詐尸還是另有隱情,我是刑警寧澤际插,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布碘耳,位于F島的核電站,受9級特大地震影響框弛,放射性物質(zhì)發(fā)生泄漏辛辨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斗搞。 院中可真熱鬧指攒,春花似錦、人聲如沸僻焚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虑啤。三九已至隙弛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狞山,已是汗流浹背全闷。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萍启,地道東北人总珠。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像勘纯,于是被迫代替她去往敵國和親局服。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔驳遵,今天18年5月份再次想寫文章淫奔,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,732評論 2 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,089評論 1 32
  • 定義注解 Kotlin使用 annotation class 關(guān)鍵字(就像使用 enum class 定義枚舉類一...
    凌寒天下獨自舞閱讀 923評論 0 0
  • 面向?qū)ο缶幊蹋∣OP) 在前面的章節(jié)中搏讶,我們學(xué)習(xí)了Kotlin的語言基礎(chǔ)知識、類型系統(tǒng)霍殴、集合類以及泛型相關(guān)的知識媒惕。...
    Tenderness4閱讀 4,419評論 1 6
  • 1. 走近你并非因都是天涯行客 數(shù)盡繽紛心中只有一道景色 2. 一生中有許多相遇 最快樂的相遇是認識了你 一生中有...
    洛長禾閱讀 330評論 0 2