注解

使用注解

使用注解只需要把@符作为名字的前缀,并放在需要注解的代码元素前面。

  • @Test
    是一个好用的注解,他可以用来测试一个方法的执行。
  • @Deprecated
    这个注解的作用是,声明那些方法已经被弃用,淘汰了。它可以与IDE一起使用。在kotlin中,他还使用了replaceWith参数加强了他。可以提供一个替代者,并且转移参数。(一条不推荐使用的消息,和一个替代者的模式)

注解能够拥有的参数

基本数据类型,字符串,枚举,类引用,其他注解。
他与java的不同:

  • 要把一个类指定为注解实参,必须在类后面加上::class,表示这个类的反射。
  • 要把另一个注解添加为实参,必须强调注解前的@。
  • 要把一个数组指定为实参,必须使用arrayOf函数
  • 注解使用的参数必须是已知的。也就是常数

注解目标


因为一个kotlin的声明往往对应着java的多个声明。如一个属性,java中只会是一个普通的字段。而kotlin中还包括set,get方法和一些隐藏的参数。所以使用点目标可以具体确定是那个代码接受注解。
Kotlin 支持的使用点目标的完整列表如下
• property一 Java 的注解不能应用这种使用 目标
• field 一 为属性生成的字段
• get 一一属性的 getter
• set 一一 属性的 setter
• receiver 一一 扩展函数或者扩展属性的接收者参数。
• param一一构造方法的参数。
• setparam一一属性 setter 的参数
• delegate 为委托属性存储委托实例的字段
• file 一一包含在文件中声明的顶层函数和属性的类。

用注解控制生成的javaAPI
可以通过注解改变kotlin默认生成的javaAPI。而自己设定
@JvmName–改变由kotlin生成的java方法或字段
@JvmStatic–将方法或属性暴露为java中的静态内容
@JvmOverloads–将kotlin编译器中带默认参数值的方法或构造方法自动重载为多个方法
@JvmField–把一个属性暴露为一个没有访问器的公有java字段

声明注解

只需在类前添加annotation
也就是annotation class,而且kotlin编译器禁止注解类有实体。也就是不能有大括号。只能有部分参数。
对于kotlin的注解,他有一个特殊的属性:name,他与java中注解的一个属性类似:value。这个属性不用强制写key-value的模式。而是可以直接写值。

元注解

顾名思义,即使可以添加在注解类上的注解,他的作用是用来控制一个注解类的使用。
最常用的就是@Target。他可以用来控制注解能用在哪些位置上。比如

1
2
@Target(AnnotationTarget.PROPERTY)
annotation class MyAnnotation

这个注解就只用使用在属性Property上。相应的还有类、文件、函数、属性、属性访 所有 表达式 等等

对于java,他不能使用property的属性。所以你必须在声明一个field才能供java使用。

使用类作为注解的参数,通常是使用他的反射,而且他还可以使用泛型类,包括他的投影

反射

一种在运行时动态的访问对象属性和方法的方式。而不需要提前知道这些属性是什么。
通常你访问一个对象的方法或属性时,程序的源代码会引用一个具体的声明,编译器会静态的解析这个引用,并确保它是存在的。但有时候,你需要编写能够使用任意类型的对象的代码,或者只能在运行时才能确定要访问的方法和属性。JSON序列化就是这样的。

两种反射

  1. java的反射。定义在java.lang.reflect中,因为kotlin会被编译为字节码,java反射完全支持它,这意味着使用了反射API的java库完全兼容kotlin代码。
  2. kotlin的反射。定义在kotlin.reflect中,他让你访问一些java中不存在的概念,如属性和空类型。但有些情况你任然会去使用java的反射

    kotlin的反射没有仅限于kotlin类,而是使用同样的APi访问任何用JVM语言编译成的类。

kotlin反射API:KClass,KCallable,KFunction,KProperty


所有反射类的继承关系如上

  1. KClass
    既可以表示类,也可以表示所有对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class People(val name: String, val age: Int) : Animal() {
    fun print() {
    println("name:$name,age:$age")
    }
    }

    val people = People("小明", 18)
    val kclass = people.javaClass.kotlin
    println(kclass.simpleName)
    kclass.members.forEach {
    println(it.name)
    }

    你可以通过获取反射然后得到他的所有成员和类名,还有更多方便的方法,这里不细说

  2. KFunction
    它接受一个方法的反射,然后它提供了一个方法call()可以用来调用这个方法,而且call()的参数是可变参数,无论你的方法有几个参数他都能够实现。但是这种方法不安全,它可能会导致类型不安全,或者参数数量不对等错误。
    因此kotlin提供了另一种方法,invoke()它会根据你的方法参数数量和类型而确定自己的参数。当然它需要KFunction1,KFunction2等等这些类型。这些类型被称为合成的编译器生成类型,它不会在包内声明。这意味着你可以使用任意数量的参数,而且它减小了jar包的尺寸。

    KFunction和Function有着类似的属性。当然他们也都可以接受lambda函数和::函数的函数体。

  3. KProperty
    首先,它必须接受一个属性,而不是普通的变量。
    其次你使用它获取属性就是通过::来获取的,也可以类似于lambda函数。因此你使用这个属性时,必须得传入一个实例进去,或者绑定引用。如果是全局的,就不需要特定的传入一个实例。