本章内容包括:
- 应用和定义注解
- 在运行时使用反射对类进行自省
- 一个真正的 Kotlin 项目实例
10.1 声明并应用注解
代码语言:javascript复制 /**-------------------------10.1.1 应用注解-------------------------*/
@Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)"))
fun remove(index: Int) {
}
/*
* 指定注解实参的语法与Java有些微小的差别:
* - 要把一个类指定为注解实参,在类名后加上 ::class:@MyAnnotation (MyClass:class)
* - 要把另一个注解指定为一个实参,去掉直接名称前面的 @。
* - 要把一个数组指定为一个实参,使用arrayOf函数:@RequestMapping(path = arrayOf("/foo","/bar"))。
*/
// @Test(timeout = 100L)
// fun testMethod() {
// }
/**-------------------------10.1.2 注解目标-------------------------*/
/*
* @get : Rule
*
* @get 使用点目标
* Rule 注解名称
*/
/*
class HasTempFolder{
// 注解的是 getter 而不是属性
@get :Rule
val folder = TemporaryFiolder()
}*/
/*
* Kotlin 支持的使用点目标的完整列表如下:
* - property -- Java的注解不能应用这种使用点目标
* - field -- 为属性生成的字段
* - get -- 属性的getter
* - set -- 属性的setter
* - receiver -- 扩展函数护着扩展属性的接收者参数
* - param -- 构造方法的参数
* - setparam -- 属性setter的参数
* - delegate -- 为委托属性存储委托实例的字段
* - file -- 包含在文件中声明的顶层函数和属性的类
*
* 用注解控制 JAVA API
* - @JvmName 将改变由Kotlin生成的Java方法或字段的名称
* - @JvmStatic 能被用在对象声明合作和伴生对象的方法上,把它们暴露成Java的静态方法
* - @JvmOverloads 指导Kotlin编译器为带默认参数值的函数生成多个重载(函数)
* - @JvmField 可以应用于一个属性,把这个属性暴露成一个没有访问器的共有Java字段
*/
/**-------------------------10.1.3 使用注解订制 JSON 序列化-------------------------*/
/*
* @JsonExclude 注解用来标记一个属性,这个属性应该排除在序列化和反序列化之外。
* @JsonName 注解让你说明代表这个属性的(JSON)键值对之中的键应该是一个给定的字符串,而不是属性的名称。
*/
// data class Person(@JsonName("alias") val firstName: String, @JsonExclude val age: Int? = null)
/**-------------------------10.1.4 声明注解-------------------------*/
// 对拥有参数的注解来说,在类的主构造方法中声明这些参数:
// annotation class JsonName(val name:String)
/*Java中声明同样的注解:*/
// public @ interface JsonName {String value(); }
/**-------------------------10.1.5 元注解:控制如何处理一个注解-------------------------*/
// 可以应用到注解类上的注解被称为 元注解。
// @ Target 元注解说明了注解可以被应用的元素类型。
// @Target(AnnotationTarget.PROPERTY_CLASS)
// annotation class JsonExclude
/*
* @Retention 元注解:
* 说明你声明的注解是否会存储到.class 文件,以及在运行时是否可以通过反射来访问它。
* Java 默认会在运行时存在,所以Kotlin的默认行为不同:注解拥有RUNTIME保留期。
* */
/**-------------------------10.1.6 使用类做注解参数-------------------------*/
/*
interface Company {
val name: String
}
data class CompanyImpl(override val name: String) : Company
data class Person(
val name: String,
@DeserializeInterface(CompanyImpl::class) val company: Company
)
annotation class DeserializeInterface(val targetClass: KClass<out Any>)
*/
/**-------------------------10.1.7 使用泛型类做注解参数-------------------------*/
// @CustomSerializer 注解接收一个自定义序列化器类的引用作为实参。这个序列化器类应该实现 ValueSerializer 接口:
// interface ValueSerializer<T> {
// fun toJsonValue(value: T): Any?
// }
10.2 反射:在运行时对Kotlin对象进行自身
代码语言:javascript复制 /**-------------------------10.2.1 Kotlin反射API:KClass、KCallable、KFunction、和KProperty-------------------------*/
// 从Java切换到Kotlin的反射API:
class Person(val name: String, val age: Int)
val person = Person("jingbin", 28)
val kClass = person.javaClass.kotlin// 返回一个 KClass<Person> 的实例
println(kClass.simpleName)// Person
kClass.memberProperties.forEach { println(it.name) }// age name
// 如何通过反射使用call来调用一个函数
// interface KCallable<out R> {
// fun call(vararg age: Any?): R
// }
fun foo(x: Int) = println(x)
val kFunction = ::foo
kFunction.call(42)
/**-------------------------10.2.2 用反射实现对象序列化-------------------------*/
// 代码清单10.1 序列化一个对象
// private fun StringBuilder.serializeObject(obj: Any) {
// // 取得对象的 XClass
// val kClass = obj.javaClass.kotlin
// // 取得类的所有属性
// val properties = kClass.memberProperties
//
// properties.joinToStringBuilder(this, prefix = "{", postfix = "}") { prop ->
// // 取得属性名
// serializeString(prop.name)
// append(": ")
// // 取得属性值
// serializePropertyValue(prop.get(obj))
// }
// }
/**-------------------------10.2.3 用注解订制序列化-------------------------*/
// 代码清单10.2 使用属性过滤序列化对象
// private fun StringBuilder.serializeObject(obj: Any) {
// obj.javaClass.kotlin.memberProperties
// .filter { it.findAnnotation<JsonExclude>() == null }
// .joinToStringBuilder(this, prefix = "{", postfix = "}") {
// serializeProperty(it, obj)
// }
// }
// 代码清单10.3 序列化单个属性
// private fun StringBuilder.serializeProperty(prop: KProperty1<Any, *>, obj: Any) {
// val jsonNameAnn = prop.findAnnotation<JsonName>()
// val propName = jsonNameAnn?.name ?: prop.name
// append(": ")
// serializePropertyValue(prop.get(obj))
// }
// 代码清单10.4 取回属性值的序列化器
// 代码清单10.5 序列化属性,支持自定义序列化器
/**-------------------------10.2.4 JSON解析和对象反序列化-------------------------*/
// 代码清单10.6 JSON解析器回调接口
// 代码清单10.7 从JSON数据创建对象的接口
// 代码清单10.8 顶层反序列化函数
// 代码清单10.9 反序列化一个对象
/**--------10.2.5 反序列化的最后一步:callBy()和使用反射创建对象-------------------------*/
// 代码清单10.10 根据值类型取得序列化器
// 代码清单10.11 Boolean值的序列化器
// 代码清单10.12 缓存的反射数据的存储
// 代码清单10.13 构造方法的参数及注解数据的缓存
// 代码清单10.14 验证需要的参数被提供了
总结
- Kotlin 中应用注解的语法和 Java 几乎一模一样。
- 在Kotlin 中可以让你应用注解的目标的范围比 Java 更广,其中包括了文件和表达式。
- 一个注解的参数可以是一个基本数据类型、一个字符串、一个枚举、一个类引用、一个其他注解类的实例,或者前面这些元素组成的数组。
- 如果单个 Kotlin 声明产生了多个字节码元素,像@get Rule 这样指定一个注解的使用点目标,允许你选择注解如何应用。
- 注解类的声明是这样的,它是一个拥有主构造方法且没有类主体的类,其构造方法中所有参数都被标记成 val 属性。
- 元注解可以用来指定(使用点)目标、保留期模式和其他注解的特性。
- 反射 API 让你在运行时动态地列举和访问一个对象的方法和属性。它拥有许多接口来表示不同种类的声明,例如类( KClass )、函数( KFunctio川等。
- 要获取一个 KClass 的实例,如果类是静态己知的,可以使用 ClassName::class :否则,使用 obj.javaClass kotlin 从对象实例上取得类。
- KFunction 接口和 KProperty 接口都继承了 KCallable ,它提供了 通用的 call 方法。
- KCallable. callBy 方法能用来调用带默认参数值的方法。
- KFunctionO、KFunctionl 等这种不同参数数量的函数可以使用 invoke方法调用。
- KPropertyO 和 KPropertyl 是接收者数量不同的属性,支持用 get 方法取回值。KMutablePropertyO 和 KMutableProperty1 继承了这些接口。支持通过 set 方法来改变属性的值。