反射是允许在运行时期访问 程序结构 的一类特性(程序结构包括:类、接口、方法、属性等)。
Kotlin
中不仅自己实现了一套 Kotlin
反射的 API
,还可以使用 Java
反射的 API
。
Kotlin
反射的 API
是一个独立的库,使用之前需要进行依赖配置:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect" // Kotlin反射 API 的依赖库
}
@MetaData
注解将 Kotlin
代码反编译成 Java
代码后,Java
代码中每个类都会被注解 @MetaData
修饰。注解 @MetaData
中保存了 Kotlin
反射相关的信息。
获取类型的所有属性、方法、内部类等信息(即通过反射来访问程序本身,所以又可以称为自省)。
通过方法的名称字符串及签名字符串来调用方法;通过属性的名称字符串来访问属性。
通过签名信息获取泛型实参的具体类型。
访问运行时注解及其信息完成注入或者配置操作。
KType
描述未擦除的类型或泛型参数等,例如 Map
。
可通过 typeOf
或者类型 KClass
、KProperty
、KFunction
获取对应的父类、属性、函数参数等。
KClass
描述对象的实际类型,不包含泛型参数,例如 Map
。
可通过对象、类型名直接获得 KClass
实例。
KProperty
描述属性。
可通过属性引用、属性所在类的 KClass
实例获取。
KFunction
描述函数。
可通过函数引用、函数所在类的 KClass
实例获取。
Kotlin
与 Java
中程序结构相关类的对比Kotlin |
Java |
---|---|
KType |
Type |
KClass |
Class |
KProperty |
Field |
KFunction |
Method |
Java
反射与 Kotlin
反射的优缺点Java
反射:
优点:无需引入额外依赖,首次使用速度相对较快。
缺点:无法访问 Kotlin
语法特性,需对 Kotlin
生成的字节码足够了解(只有了解其字节码才能知道 Java
反射中如何处理 Kotlin
代码)。
Kotlin
反射:
优点:支持访问 Kotlin
几乎所有特性,API
设计更友好。
缺点:需要额外引入 Kotlin
反射依赖库(2.5M
,编译后 400K
),首次调用较慢(需要解析注解 @MetaData
中保存的反射信息)。
类名::class
” 获取 KClass
& 通过 KClass
获取程序结构信息通过 类名::class
可以获取到 Kotlin
类的 KClass
实例。
KClass
类中提供了大量 API
来访问类的程序结构信息(如属性,方法,内部类,等等)。
typeOf
获取 KType
& 通过 KType
获取泛型信息通过 Kotlin
反射依赖库提供的全局函 typeOf
获取泛型类型的相关信息。
Kotlin
中的泛型是 伪泛型,即经过编译后的类型上就不会带泛型了。
但是为什么通过 KType
实例还能够拿到类型的泛型信息呢?
这是因为 Kotlin
代码经过编译后会为擦除掉的泛型生成一个签名信息(如下图的 Kotlin
字节码中的 signature
所示):
通过 KType
实例获取到的泛型信息就是通过解析 signature
签名得到的。
在打包混淆时,泛型的签名信息会被混淆。
因此,为了保证通过 KType
实例能够获取到泛型信息,应该在混淆配置文件中保留 signature
签名信息不被混淆:
//Proguard配置
-keepattributes Signature
Model
映射需求:实现具有相同属性列表的不同数据类之间的转换。
KProperty.getDelegate()
)调用 KProperty
类的 getDelegate()
方法获取属性代理实例。
isAccessible = true
)调用 getDelegate
方法之前,需要设置 isAccessible = true
,表示阻止属性的访问权限检查。