Kotlin 之反射详解

  • 反射是允许程序在运行时访问程序结构的一类特性

  • 程序结构包括:类,接口,方法,属性等语法特性

Kotlin 字节做了一套反射API库,这个库需要进行依赖

implementation("org.jetbrains.kotlin:kotlin-reflect")

反射的常见用途

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

反射常用的数据结构

数据结构 概念及说明使用
KType 描述未擦除的类型或泛型参数等,例如Map<String,Int>;可以通过typeOf或者一些类型获取对应父类,属性,函数参数等
KClass 描述对象的实际类型,不包含泛型参数,例如 Map;可通过对象,类型名直接获得
KProperty 描述属性,可通过属性引用,属性所在类的 KClass 获取
KFunction 描述函数,可通过函数引用,函数所在类的 KClass 获取

获取泛型实参

fun main() {
    /**
     * declaredFunctions:获取该类中所有的函数
     * first:判断是否为指定的函数名,如果相等则返回出去
     * returnType:获取返回值
     * arguments:返回值的泛型
     */
    Api::class.declaredFunctions.first { it.name == "getUser" }
        .returnType.arguments.forEach {
        println(it.type)
    }

    //直接拿到函数引用,其他的和上面一样
    Api::getUser.returnType.arguments.forEach {
        println(it)
    }

    val subType = SubType()
    subType.typeParameter.let { println(it) }
}

class UserDTO

interface Api {
    fun getUser(): List<UserDTO>
}


abstract class SuperType<t> {
    val typeParameter by lazy {
        /**
         * 抽象类不能有实例,所以这里的 this 是子类的
         * supertypes:获取父类类型列表
         * first:获取第一个
         * arguments:拿到泛型
         * type:类型
         */
        this::class.supertypes.first().arguments.first().type!!
    }
    val typeParameterJava by lazy {

    }
}

class SubType : SuperType<String>()

案例:深拷贝

data class Person(val id: Int, val name: String, val group: Group)

data class Group(val id: Int, val name: String, val location: String)

fun main() {

    val person = Person(
        0,
        "345",
        Group(0, "Kotlin.cn", "China")
    )
    val deep = person.deepCopy()


    val copied = person.copy()


    println(person.group === copied.group)
    println(person.group === deep.group)
    println(deep)
}

fun <T : Any> T.deepCopy(): T {
    //如果不是数据类就直接返回
    if (!this::class.isData) {
        return this
    }
    /**
     * primaryConstructor:获取该类的主构造函数,如果没有返回 null
     */
    return this::class.primaryConstructor!!.let { primaryConstuctor ->
        /**
         * parameters:构造方法中的参数
         */
        primaryConstuctor.parameters.map { parameter ->

            /**
             * memberProperties:,类中所有非扩展属性
             */
            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
            }
          //call/callBy 创建对象                                
        }.toMap().let(primaryConstuctor::callBy)
    }
}

案例:映射

data class UserVO(val login: String, val avatarUrl: String)

data class UserDTO(
    var id: Int,
    var login: String,
    var avatarUrl: String,
    var url: String,
    var htmlUrl: String
)

fun main() {
    val userDTO = UserDTO(
        0,
        "345",
        "https://www.baidu.com",
        "https://github",
        "https://httpurl"
    )

    val userVo: UserVO = userDTO.mapAs()
    println(userVo)

    val userMap = mapOf(
        "id" to 0,
        "login" to "1",
        "avatarUrl" to "2",
        "url" to "3"
    )
    val userVOFromMap: UserVO = userMap.mapAs()
    println(userVOFromMap)
}

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 { kParameter ->
            //如果接受null,则返回,否则抛出异常
            //this[kParameter.name] :从当前的 map 中寻找。如果找到了则就是拿到了 value,否则异常
            kParameter to (this[kParameter.name] ?: if (kParameter.type.isMarkedNullable) null
            else throw IllegalArgumentException("失败"))
        }.toMap().let(it::callBy)//构建对象
    }
}

案例:释放对象引用不可空类型

class ReleasableNotNull<T : Any> {

    private var value: T? = null

    operator fun getValue(thisRef: Any, kProperty: KProperty<*>): T {
        return value ?: throw IllegalArgumentException("使用错误")
    }

    operator fun setValue(thisRef: Any, kProperty: KProperty<*>, value: T) {
        this.value = value
    }

    fun isInitialized() = value != null

    fun release() {
        value = null
    }
}

//扩展属性
inline val KProperty0<*>.isInitialized: Boolean
    get() {
        isAccessible = true
        return (this.getDelegate() as ReleasableNotNull<*>).isInitialized()
    }

//扩展方法
fun KProperty0<*>.release() {
    isAccessible = true
    (this.getDelegate() as ReleasableNotNull<*>).release()
}

class Bitmap(val with: Int, val height: Int)

class Activity {
    private var bitmap by ReleasableNotNull<Bitmap>()

    fun onCreate() {
        println(::bitmap.isInitialized)
        bitmap = Bitmap(234, 353)
        println(::bitmap.isInitialized)
    }

    fun onDestroy() {
        println(::bitmap.isInitialized)
        ::bitmap.release()
        println(::bitmap.isInitialized)
    }
}

fun main() {
    val activity = Activity()
    activity.onCreate()
    activity.onDestroy()
}
false
true
true
false

通过反射调用构造函数、

fun main() {

    //获取对象的class
    val cls = Person(23, "张三").javaClass.kotlin

    //调用构造器 2
    val cons = cls.primaryConstructor
    val per1 = cons?.call(100, "李四")
    println(per1)
    //2
    val per2 = cls.primaryConstructor.let {
        var i = 0
        val s = it!!.parameters.map {
            i++
            when (i) {
                1 -> it to 2
                2 -> it to "哈哈"
                else ->
                    it to ""
            }

        }.toMap()
        it.callBy(s)
    }
    println(per2)


}

class Person(val age: Int, val name: String) {

    val p = 1

    fun a() {}

    private fun b() {}

    override fun toString(): String {
        return "姓名:$name 年龄:$age"
    }
}
  • 反射
    • 反射是啥
    • 数据结构:KType,KClass,KProperty,KFunction
    • 获取泛型实参
      • 了解泛型的实现机制
      • 料及实参在字节码中的存储机制
      • 掌握反射获取对应类型的 KType 进而读取泛型实参的方法
    • 为数据类添加深拷贝
    • 反射调用构造器构造数据类实例
    • 实例之间的转换
    • 可释放引用的不可空类型

参考自慕课网视频

原创文章 119 获赞 77 访问量 3万+

猜你喜欢

转载自blog.csdn.net/baidu_40389775/article/details/105496788