声明并应用注解
应用注解
同Java一样,注解放到函数或类最前面
class MyTest {
@TestOnly
fun test() {
}
}
@Deprecated可以提供实参,且通过ReplaceWith为IDEA提示正确的用法
class MyTest{
@Deprecated("Use removeAt(index) instead.",ReplaceWith("removeAt(index)"))
fun remove(index: Int) {
}
fun removeAt(index: Int) {
}
}
注解的参数类型为:基本数据类型、字符串、枚举、类引用、其他的注解类,以及他们的数组,但需要注意
- 类作为参数时,类名后加上::class
- 注解作为参数时,不用加上@,如上面Deprecated中的ReplaceWith也是一个注解
- 数组作为参数时,需使用arrayOf函数,如@RequestMapping(path = arrayOf(“/foo”,“/bar”))
- 注解实参需要在编译期已知,若使用属性则需使用const修饰为常量
注解目标
Koltin的单个声明会对应Java多个声明(如一个Kotlin属性对应一个Java字段、getter、setter)
要限制注解的使用范围,需通过使用点目标声明,使用点目标被放在@符号和注解名词之间,并用冒号隔开,如下表示注解应用于属性的getter
完整的使用点目标如下
- property——Java 的注解不能应用这种使用点目标
- field——为属性生成的字段
- get 一一属性getter
- set一一属性setter
- receiver 一一扩展函数或者扩展属性的接收者参数
- param一一构造方法的参数
- setparam一一属性setter的参数
- delegate一一为委托属性存储委托实例的字段
- file一一包含在文件中声明的顶层函数和属性的类,需放在package之前
Java的注解只能用于类和函数的声明,而Kotlin允许对任意的表达式应用注解,如用于局部变量
- @Volatile、@Strictfp代替Java的volatile、strictfp关键字
- @file:JvmName(“Abc”)用于修改顶层函数生成的类的名称,否则Xxx.kt默认为XxxKt.class,添加注解后为Abc.class
- @JvmStatic用在对象声明或者伴生对象的方法上,让其变成Java的静态方法
- @JvmOverloads为带默认参数值的函数生成多个重载函数
- @JvmField将属性变成一个公有Java字段
注解声明
以下用加载JSON的JKid库中的注解进行讲解,注解没有类实体,若存在参数,则必定为val
annotation class JsonExclude //不参与序列化
annotation class JsonName(val name: String) //修改JSON中key-value中的key
元注解
注解类自己也可以被注解,应用到注解类上的注解被称为元注解,如下使用@Target标识@JsonExclude只能用于属性
- Java中无PROPERTY,如果属性需要用于Java,则需要指定FIELD
@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude
若要声明自己的元注解,需要使用AnnotationTarget.ANNOTATION_CLASS
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class BindingAnnotation
@BindingAnnotation
annotation class MyBinding
@Retention元注解在Java中表示注解是否会存储到.class文件以及能否在运行时通过反射来访问,而Kotlin默认注解有Rumtime保留期
使用类作为注解参数
需要使用out指定可接受子类型,否则只能使用Any
annotation class DeserializeInterface(val targetClass: KClass<out Any>)
使用泛型类作为注解参数
默认情况下,非基本数据类型的属性会被当成嵌套的对象序列,如果需要自定义序列化方式,则应该实现序列化器接口ValueSerializer,同时@CustomSerializer支持所有ValueSerializer的泛型子类
interface ValueSerializer<T> {
fun toJsonValue(value: T): Any?
fun fromJsonValue(jsonValue: Any?): T
}
annotation class CustomSerializer(val serializerClass: KClass<out ValueSerializer<*>>)
反射
反射是一种运行时动态地访问对象属性和方法的方式
KClass
可通过如下两种方式获取KClass
val kClass1 = Person::class
println(kClass1.simpleName)
val person = Person("A", 1)
val kClass2 = person.javaClass.kotlin
println(kClass2.simpleName)
KCallable
可通过members获取所有属性和方法(需根据IDEA提示添加kotlin-reflect.jar),其都是KCallable的子类型
class Person(val name: String, val age: Int)
val kClass = Person::class
val members = kClass.members
members.forEach { println(it) }
如上打印(代码在Android的MainActivity运行)
val com.demo.demo1.`MainActivity$onCreate$Person`.age: kotlin.Int
val com.demo.demo1.`MainActivity$onCreate$Person`.name: kotlin.String
fun com.demo.demo1.`MainActivity$onCreate$Person`.equals(kotlin.Any?): kotlin.Boolean
fun com.demo.demo1.`MainActivity$onCreate$Person`.hashCode(): kotlin.Int
fun com.demo.demo1.`MainActivity$onCreate$Person`.toString(): kotlin.String
KFunction
::foo是一个KFunction1<Int,Unit>实例,1表示参数个数,<int,Unit>表示参数和返回值类型,继承KCallable,其call()方法允许你调用对应的函数或者对应属性的getter
fun foo(x: Int) = println(x)
val kFunction = ::foo
kFunction.call(1)
call()不提供类型检查和参数数量检查,若传入错误的值,会在运行时报错,而通过invoke()或直接调用KFunction可在编译时提示错误
fun sum(x: Int, y: Int) = x + y
val kFunction = ::sum
println(kFunction.invoke(1, 2) + kFunction(3, 4))
KProperty
顶层属性为KProperty0接口实例,可通过call()调用getter,或通过反射调用setter,也可直接调用get()
var counter = 0
val kProperty = ::counter
println(kProperty.call())
kProperty.setter.call(21)
println(kProperty.get())
成员属性为KProperty1<Person,Int>接口实例,访问时需提供所属对象的实例
class Person(val name: String, val age: Int)
val person = Person("A", 1)
val kProperty = Person::age
println(kProperty.get(person))
获取注解