Kotlin注解

声明注解

Kotlin中声明主角用 annotation class 关键字,注解也是一种class,编译器同样可以对注解类型在编译器进行类型检查,实例如下:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class Test {

}
  • 自定义注解中的 @Target @Retention 等称之为元注解(Meta - annotation)。元注解说明如下
元注解名称 功能说明
@Target 指定这个注解可被用于哪些元素(在public enum class AnnotationTarget 中定义了枚举)类Class、注解类ANNOTATION_CLASS、泛型参数TYPE_PARAMETER,函数FUNCTION、属性PROPERTY、成员变量FIELD、局部变量LOCAL_VARIABLE、VALUE_PARAMETER、CONSTRUCTOR、PROPERTY_GETTER、PROPERTY_SETTER,接口描述类、接口、enum的TYPE,表达式EXPRESSION,文件FILE,类型别名TYPEALIAS
@Retenion 指定这个注解的信息是否被保存到编译后的class文件中,以及在运行时是否可以通过反射访问到它。可取值有三个:SOURCE(注解数据不存储在二进制输出中),BINARY(注解的数据存储到二进制输出中,但是反射不可见。),RUNTIME(注解数据存储在二进制输出中,可用于反射,默认值是这个)
@Repeatable 允许在单个元素上多次使用同一个注解
@MustBeDocumented 表示这个注解是公开API的一部分,在自动生成API文档的类或者函数签名中,应该包含这个注解信息
使用注解

使用方式如下

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestRun {

}

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String) {

}

@TestRun
class Test {
    @TestCase("1")
    fun testCase(testId: String) {
        println(testId)
    }

}

其中,注解参数可以支持的类型如下

  1. 基本数据类型
  2. String类型
  3. KClass类型
  4. enum类型
  5. Annotation
  6. 以及以上类型(除基本数据类型)的数组。
annotation class TestCase(val id: String){}
annotation class TestCase(val id: Int){}
annotation class TestCase(val id: Array<String>){}
annotation class TestCase(val id: TestRun){}
annotation class TestCase(val id: KClass<String>){}

以下编译不通过

annotation class TestCase(val id: Array<Int>){}
annotation class TestCase(val id: Test){}

注解中不能有null类型,因为JVM不支持将null作为注解属性值就行存储。如果注解用作另一个注解时,名称不能以@字符为前缀。

annotation class AnnX {}

annotation class AnnY(val message: String,
                      val annX: AnnX = AnnX()) {
}

java注解和Kotlin注解完全兼容。

处理了注解
  • 定义了注解,并在需要的时候给相关位置添加上注解信息后,如何让注解发挥作用,核心就在于处理注解的代码了。接下来我们了解一下注解的获取和处理。

以上面的两个注解为例,调用如下

@TestRun
class AnnTest {
    @TestCase("1")
    fun test(textId: String) {

    }
}

1. ::class引用
首先声明一个变量指向AnnTest

val annTest = AnnTest()

然后通过这个变量来获取该对象的类的信息。使用::class来获取KClass的引用

val kClass= annTest::class

2. declaredFunctions扩展属性
下面我们要获取annTest 对象类型所声明的所有函数。Kotlin中直接使用扩展属性declaredFunctions来获取这个类中声明的所有函数(对应的反射类型是KFunction)。

 val declaredFunctions = kClass.declaredFunctions

返回的是一个Collection<KFunction<*>>其中< * >是Kotlin中的星投影,类似于java中的<?>通配符。
declaredFunctions源码如下:

扫描二维码关注公众号,回复: 8974722 查看本文章
@SinceKotlin("1.1")
val KClass<*>.declaredFunctions: Collection<KFunction<*>>
    get() = (this as KClassImpl).data().declaredMembers.filterIsInstance<KFunction<*>>()

3. annotations属性

public interface KFunction<out R> : KCallable<R>, Function<R>

KFunction继承了KCallable,KCallable又继承了KAnnotatedElement。KAnnotatedElement中有个annotations: List< Annotation >方法

public interface KAnnotatedElement {
    /**
     * 存储了该函数所有注解信息。
     */
    public val annotations: List<Annotation>
}

通过遍历这个List可以获取到@TestCase注解。

for (f in declaredFunctions) {
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id
                println(id) //可以自己做一些操作了
               
            }
        }
    }
  1. call函数
    如果想通过反射来调用函数,可以直接使用call()函数
 f.call(annTest,id)

等价于 f.javaMethod?.invoke(annTest,id)

完整代码如下:

import kotlin.reflect.full.declaredFunctions

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestRun {

}

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class TestCase(val id: String) {

}

@TestRun
class AnnTest {
    @TestCase("1")
    fun test(textId: String) {

    }
}

fun parseAnn(){
    val annTest = AnnTest()
    val kClass = annTest::class
    val declaredFunctions = kClass.declaredFunctions

    for (f in declaredFunctions) {
        f.annotations.forEach {
            if (it is TestCase) {
                val id = it.id
                println(id)
                f.call(annTest,id)
            }
        }
    }
}

fun main(args: Array<String>) {
    parseAnn()
}

测试代码如下


fun main(args: Array<String>) {
    parseAnn()
}
发布了119 篇原创文章 · 获赞 28 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ldxlz224/article/details/97951077