1. 什么是反射
反射是允许在运行时期访问 程序结构 的一类特性(程序结构包括:类、接口、方法、属性等)。
2. 反射的依赖库
Kotlin 中不仅自己实现了一套 Kotlin 反射的 API,还可以使用 Java 反射的 API。
Kotlin 反射的 API 是一个独立的库,使用之前需要进行依赖配置:
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-reflect" // Kotlin反射 API 的依赖库
}
 

3. @MetaData 注解
 
将 Kotlin 代码反编译成 Java 代码后,Java 代码中每个类都会被注解 @MetaData 修饰。注解 @MetaData 中保存了 Kotlin 反射相关的信息。
4. 反射的使用场景
-  
获取类型的所有属性、方法、内部类等信息(即通过反射来访问程序本身,所以又可以称为自省)。
 -  
通过方法的名称字符串及签名字符串来调用方法;通过属性的名称字符串来访问属性。
 -  
通过签名信息获取泛型实参的具体类型。
 -  
访问运行时注解及其信息完成注入或者配置操作。
 
5. 反射中与程序结构相关的类
5.1 KType
 
描述未擦除的类型或泛型参数等,例如 Map<String, Int>。
可通过 typeOf 或者类型 KClass、KProperty、KFunction 获取对应的父类、属性、函数参数等。
5.2 KClass
 
描述对象的实际类型,不包含泛型参数,例如 Map。
可通过对象、类型名直接获得 KClass 实例。
5.3 KProperty
 
描述属性。
可通过属性引用、属性所在类的 KClass 实例获取。
5.4 KFunction
 
描述函数。
可通过函数引用、函数所在类的 KClass 实例获取。
5.5 Kotlin 与 Java 中程序结构相关类的对比
 
Kotlin | Java | 
|---|---|
KType | Type | 
KClass | Class | 
KProperty | Field | 
KFunction | Method | 
6. Java 反射与 Kotlin 反射的优缺点
 
Java 反射:
-  
优点:无需引入额外依赖,首次使用速度相对较快。
 -  
缺点:无法访问
Kotlin语法特性,需对Kotlin生成的字节码足够了解(只有了解其字节码才能知道Java反射中如何处理Kotlin代码)。 
Kotlin 反射:
-  
优点:支持访问
Kotlin几乎所有特性,API设计更友好。 -  
缺点:需要额外引入
Kotlin反射依赖库(2.5M,编译后400K),首次调用较慢(需要解析注解@MetaData中保存的反射信息)。 
7. 反射的使用举例
7.1 通过 “类名::class” 获取 KClass & 通过 KClass 获取程序结构信息
 
通过 类名::class 可以获取到 Kotlin 类的 KClass 实例。
KClass 类中提供了大量 API 来访问类的程序结构信息(如属性,方法,内部类,等等)。

7.2 通过 typeOf 获取 KType & 通过 KType 获取泛型信息
 
通过 Kotlin 反射依赖库提供的全局函 typeOf 获取泛型类型的相关信息。

7.3 获取类方法的返回值类型上的泛型实参

7.4 获取父类类型上的泛型实参

7.5 通过反射获取泛型实参的原理
Kotlin 中的泛型是 伪泛型,即经过编译后的类型上就不会带泛型了。
但是为什么通过 KType 实例还能够拿到类型的泛型信息呢?
这是因为 Kotlin 代码经过编译后会为擦除掉的泛型生成一个签名信息(如下图的 Kotlin 字节码中的 signature 所示):

通过 KType 实例获取到的泛型信息就是通过解析 signature 签名得到的。
7.6 保留泛型签名信息的混淆配置
在打包混淆时,泛型的签名信息会被混淆。
因此,为了保证通过 KType 实例能够获取到泛型信息,应该在混淆配置文件中保留 signature 签名信息不被混淆:
//Proguard配置
-keepattributes Signature
 
8. 案例
8.1 数据类的深拷贝
8.1.1 相关知识点
8.1.1.1 反射获取类的主构造器
8.1.1.2 反射获取函数的形参列表
8.1.1.3 反射调用函数
8.1.1.4 反射获取对象中属性的属性值
8.1.2 代码实现

8.2 Model 映射
 
需求:实现具有相同属性列表的不同数据类之间的转换。
8.2.1 相关知识点
8.2.1.1 判断类型是否为可空类型
8.2.2 代码实现

8.3 通过属性代理实现对不可空类型属性的资源释放
8.3.1 相关知识点
8.3.1.1 获取属性代理(KProperty.getDelegate())
 
调用 KProperty 类的 getDelegate() 方法获取属性代理实例。
8.3.1.2 阻止属性的访问权限检查(isAccessible = true)
 
调用 getDelegate 方法之前,需要设置 isAccessible = true,表示阻止属性的访问权限检查。
8.3.2 代码实现











