)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)
@Target(AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class Run
从这个关键字上可以看出注解也是一种class,编译器同样可以对注解类型在编译器进行类型检查。
自定义的注解中使用的注解,称之为元注解。通过向注解类添加元注解的方式来指定其他属性。元注解说明如表:
| 元注解名称 | 功能说明 |
| — | — |
| @Target | 指定这个注解可被用于哪些元素 ( 这些元素定义在kotlin.annotation.AnnotationTarget 枚举类中。它们是:类 CLASS, 注解类 ANNOTATION_CLASS,泛型参数 TYPE_PARAMETER,函数 FUNCTION, 属性 PROPERTY, 用于描述域成员变量的 FIELD,局部变量 LOCAL_VARIABLE,VALUE_PARAMETER,CONSTRUCTOR,PROPERTY_GETTER,PROPERTY_SETTER, 用于描述类、接口(包括注解类型) 或enum声明的 TYPE, 表达式 EXPRESSION,文件 FILE,类型别名TYPEALIAS等 |
| @Retention | 指定这个注解的信息是否被保存到编译后的 class 文件中, 以及在运行时是否可以通过反射访问到它, 可取的枚举值有3个,分别是: SOURCE (注解数据不存储在二进制输出),BINARY(注解数据存储在二进制输出中, 但反射不可见), RUNTIME(注解数据存储在二进制输出中, 可用于反射 (默认值 ) |
| @Repeatable | 允许在单个元素上多次使用同一个注解 |
| @MustBeDocumented | 表示这个注解是公开 API 的一部分, 在自动产生的 API 文档的类或者函数签名中, 应该包含这个注解的信息。 |
2.2 使用注解
上面我们声明了Run注解,它可以使用在 CLASS、FUNCTION、VALUE_PARAMETER和EXPRESSION上,我们这里给出的示例使用在类上:
@Run
class SwordTest()
我们声明的 TestCase注解有个构造函数,传入的参数时一个String类型的ID,把这个注解用在函数上:
@Run
class SwordTest() {
@TestCase(id = “a”)
fun testCase(testId: String) {
println(“Run SwordTest Id = $testId”)
}
}
上面是注解在代码中的简单使用示例。其中的 @TestCase(id=“a”)是注解构造函数的使用。
注解可以带有参数的构造器。注解参数可支持的数据类型如下:
基本数据类型
String
KClass
enum
Annotation
上面的除了基本数据类型为引用的 数组
下面两种声明是不会通过的
annotation class TestCase1(val id: Array)
annotation class TestCase2(val id: SwordTest)
另外需要注意的是,注解类型中不能有null,因为 JVM不支持将null作为注解属性的值进行存储。
如果注解用作另一个注解的参数时,则其名称不能以 @字符作为前缀。
例如:
annotation class AnnoX(val value: String)
annotation class AnnoY(
val message: String
val annoX: AnnoX = AnnoX(“X”))
2.3 处理注解
如果没有相应的注解信息处理逻辑流程,那么注解可以说是废了,没有什么实用价值。
首先,我们的目标测试类是:
@Run
class SwordTest() {
@TestCase(id = “a”)
fun testCase(testId: String) {
println(“Run SwordTest Id = $testId”)
}
}
我们需要在 @TestCase注解作用在函数上的处理过程
::class
引用首先声明一个变量指向SwordTest实例
然后就可以通过这个变量来获取该对象的类的信息,使用 ::class
来获取 sword对象实例的 KClass类的引用
val sword = SwordTest()
val kClass = sword::class
//有点像Java中的 getClass()
//上面这行代码,Kotlin编译器会自动推断出 kClass变量的类型是
val kClass:KClass = sword::class
下面我们需要获取 sword对象类型所声明的所有函数。Kotlin中 可以直接使用扩展属性 declaredFunctions来获取这个类中声明的所有函数。
//返回的是一个 Collection<KCallable<>> 其中是Koltlin泛型中的星投影
val members = kClass.members
KFunction 类型继承了 KCallable,KCallable又继承了 KAnnotatedElement。KAnnotatedElement中有个public val annotations:List
属性里存储了该函数所有的注解信息。通过遍历这个存储Annotation的List,可以获取到TestCase注解:
for (f in members ) {
f.annotations.forEach {
if(it is TestCase){
val id = it.id //TestCase 注解的属性ID
doSomething(id) //注解处理逻辑
}
}
}
另外,如果想通过反射来调用函数,可以直接使用 call():
f.call(sword, id)
//等价于
f.javaMethod?.invoke(sword, id)
//到这里,我们就完成了一个简单的注解处理器,完整的代码如下:
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.EXPRESSION
)
@Retention(AnnotationRetention.SOURCE)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String)
class SwordTest {
@TestCase(id = “a”)
fun testCase(testId: String) {
println(“Run SwordTest Id = $testId”)
}
}
fun testAnnoProcessing() {
val sword = SwordTest()
val kClass = sword::class
val members = kClass.members
for (f in members) {
f.annotations.forEach {
if (it is TestCase) {
val id = it.id
doSomething(id)
f.call(sword, id)
}
}
}
}
private fun doSomething(id: String) {
println(“Do Something in Annotation Processing $id ${System.currentTimeMillis()}”)
}
//测试:
main(){
testAnnoProcessing()
}
3. 反射
在Kotlin中我们有两种方式来实现反射功能。
一种是调用Java的反射包下的API
另外一种是 直接调用Kotlin语言提供的 kotlin.reflect包下面的API
不过因为反射功能的应用场景并非所有编程场景都会用到,所以Kotlin把 kotlin.reflect包放到了单独的 kotlin-reflect-1.1.xx.jar下面,也就是说我们如果要使用Kotlin的反射Api,还要去添加依赖。
3.1 类引用
我们先定义一个代码实例:
open class BaseContainer
class Container<T : Comparable> : BaseContainer {
val elements: MutableList
constructor(elements: MutableList) {
this.elements = elements
}
fun sort(): Container {
elements.sort()
一转眼时间真的过的飞快。我们各奔东西,也各自踏上了自己的旅途,但是即使多年不见,也因为这份情谊我们依旧如从前那般“亲密”。不忘初心方得始终。加油吧,程序员们,在我看来35岁,40岁从来不是危机,只要永远不要忘记自己为何踏上征程!
为了让更多在学习中或者最近要准备面试的朋友们看到这篇文章,希望你们能多多评论,点赞+转发!
再次感谢所有给我提供过题目的朋友们,感谢一路有你!