反射概念
- 反射是允许程序在运行时访问程序结构的一类特性
- 程序结构包括:类,接口,方法,属性等语法特性
反射常用用途
- 列出类型的所有属性,方法,内部类等
- 调用给定名称及签名的方法或访问指定名称的属性
- 通过签名信息获取泛型实参具体类型
- 访问运行时注解及其信息完成注入或者配置
反射常用数据结构
数据结构 | 概念及使用说明 |
---|---|
KType | 描述为擦除的泛型类型或者参数,例如 Map<String,String> ,可通过typeOf()或者以下类型获取对应的父类属性 |
KClass | 描述对象的实际类型,不包含泛型参数,如Map,可通过对象类型名获得 |
KProperty | 描述属性,可通过属性引用,属性所在类的KClass获取 |
KFunction | 描述函数,可通过函数引用,函数所在类的KClass获取 |
Kotlin使用反射
基本使用
引入依赖
implementation "org.jetbrains.kotlin:kotlin-reflect"
//获取KClass
var cls: KClass<String> = String::class
//获取KProperty
val property = cls.declaredMemberProperties.firstOrNull()
//获取KMap
val mapType = typeOf<Map<String, Int>>()
mapType.arguments.forEach {
println(it)
}
一般::
表示引用,后面是函数名表示引用函数,后面是属性表示引用属性
fun add(){
}
class A {
val age: Int = 10
}
val x: Int = 1
fun main() {
//引用方法
::add
//引用属性
::x
//引用类的成员属性
A::age
}
获取泛型实参
目标:获取擦除的泛型参数
interface Api {
fun getUsers(): List<UserDTO>
}
abstract class SuperType<T> {
}
class SubType : SuperType<String>()
我们先获取这个UserDTO
这个实参
//间接拿到getUsers方法
Api::class.declaredFunctions.first { it.name == "getUsers" }
.returnType.arguments.forEach {
println(it)
}
//直接拿到getUsers方法
Api::getUsers.returnType.arguments.forEach {
println(it)
}
//java获取方式
Api::class.java.getDeclaredMethod("getUsers")
.genericReturnType.safeAs<ParameterizedType>()?.actualTypeArguments?.forEach {
println(it)
}
//声明扩展方法,方便使用
fun <T> Any.safeAs(): T? {
return this as? T
}
获取SuperType
泛型
//kotlin获取方式
abstract class SuperType<T> {
val typeParameter by lazy {
this::class.supertypes.first().arguments.first().type
}
//java获取方式
val typeParameterJava by lazy {
this.javaClass.genericSuperclass.safeAs<ParameterizedType>()!!.actualTypeArguments.first()
}
}
数据类实现DeepCopy
我们先定义两个数据类
data class Person(val id: Int, val name: String, val group: Group)
data class Group(val id: Int, val name: String, val location: String)
这个Person
类中有一个参数是Group
我们创建两个对象
val person = Person(
0,
"Greathfs",
Group(
0,
"Kotlin",
"China"
)
)
val copiedPerson = person.copy()
//true
println(person.group === copiedPerson.group)
如果我们调用copy
方法获取一个Person
对象的话,那么这个Person
里面的Group
和我们创建的Person
里面的Group
是一样的,这样我们改动一个,另外一个就会有影响,我们有的时候不希望这样,所以我们为他实现一个deepCopy
方法
主要思路:
1.所有的数据类都有私有构造器;
2.通过构造器拿到参数
3.获取构造器参数对应的值
4.判断这个值是不是数据类,如果是,递归调用
5.调用构造器的callBy
方法
fun <T : Any> T.deepCopy(): T {
//不是数据类,直接返回
if(!this::class.isData){
return this
}
return this::class.primaryConstructor!!.let { primaryConstructor ->
//构造器参数,参数又是属性
primaryConstructor.parameters.map { parameter ->
//获取参数对应的值
val value = (this::class as KClass<T>).memberProperties.first { it.name == parameter.name }
.get(this)
//判断是不是数据类
if((parameter.type.classifier as? KClass<*>)?.isData == true){
parameter to value?.deepCopy()
} else {
parameter to value
}
}.toMap()
.let(primaryConstructor::callBy)
}
}
Model映射
大体思路都差的不多,都是通过构造方法拿到属性参数,遍历参数,转化成map
,最后调用callBy
inline fun <reified From : Any, reified To : Any> From.mapAs(): To {
return From::class.memberProperties.map { it.name to it.get(this) }
.toMap().mapAs()
}
inline fun <reified To : Any> Map<String, Any?>.mapAs(): To {
return To::class.primaryConstructor!!.let {
it.parameters.map {
parameter ->
parameter to (this[parameter.name] ?: if(parameter.type.isMarkedNullable) null
else throw IllegalArgumentException("${parameter.name} is required but missing."))
}.toMap()
.let(it::callBy)
}
}