阅读 660

【Kotlin】 自学(八)-Kotlin反射

反射概念

  • 反射是允许程序在运行时访问程序结构的一类特性
  • 程序结构包括:类,接口,方法,属性等语法特性

反射常用用途

  • 列出类型的所有属性,方法,内部类等
  • 调用给定名称及签名的方法或访问指定名称的属性
  • 通过签名信息获取泛型实参具体类型
  • 访问运行时注解及其信息完成注入或者配置

反射常用数据结构

数据结构概念及使用说明
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)
    }
}
复制代码
文章分类
Android
文章标签