- 聲明注解
- 案例:使用元注解
- 注解目標聲明
- 案例:讀取運行時注解信息
??與基本注解不同漠畜,在一般的應(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行的 description
是 MyAnnotaion
注解的屬性。
- 第二個注解洛心,用來注解類中成員屬性和函數(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
注解的屬性是 type
和 description
璧瞬,type
類型是 KClass<*>
,默認值是 Unit::class
渐夸。description
類型是 String
嗤锉,沒有設(shè)置默認值。
??提示:代碼第3行的 KClass<*>
類型表示 KClass 的泛型墓塌,*
是泛型通配符瘟忱,可以是任何類型。多數(shù)情況下泛型尖括號中指定的都是某個具體類型苫幢,泛型也是為此而設(shè)計的访诱。但是有時確實不需要知道具體類型,或者說什么類型都可以韩肝,此時可以使用 *
通配所有類型触菜。
- 使用
MyAnnotation
和MemberAnnotation
注解的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
注解。