美文网首页收藏
kotlin 注解和反射

kotlin 注解和反射

作者: 搬码人 | 来源:发表于2021-10-26 19:26 被阅读0次

    反射

    在程序的运行过程中动态地调用类的属性和方法:对于任意一个类,都能够知道这个类的所有属性和方法,都能够调用他的任意一个属性和方法,这种动态地获取信息以及动态调用对象的方法的功能。


    反射流程

    我们通过编译工具码代码写的文件 -> .kt/.java
    通过类加载器classLoader将上述文件加载到内存中
    然后进一步加载为可执行的文件 -> 反射就作用于这一区域,也就是字节码阶段。

         //1、只知道类名
        val clz = Person::class
        //2、已经知道某个对象
        val xw = Person()
        val clz2 = xw.javaClass.kotlin
        //可以通过class对象获取类的详细信息
        println(clz2.simpleName)
    
    打印信息

    反射中常用的属性、方法

    属性/方法 作用
    primaryConstructor 获取主构造函数
    constructors 获取所有构造函数
    memberFunctions 获取自己和父类的所有方法
    declaredFunctions 获取自己声明的方法
    functions 获取自己的所有方法(包括静态方法)和父类的所有非静态方法
    findAnnotation 检索某个注解
    annotation 访问注解
    反射的实战理解

    我们会有这样一种需求:我们有一个函数,需要获取一个对象,但是并不知道这个对象的具体类型,这个时候

    • 使用默认的构造函数创建
    //创建对象
    fun createObj(clz:KClass<out Any>):Any{
        //1、使用默认的构造函数创建
        //必须提供无参的构造函数
        return clz.createInstance()
    }
    
    • 当构造函数有参数时
    fun main() {
    
       
       val lw = createObj(Student2::class) as Student2
        println(lw.name)
    }
    
    //创建对象
    fun createObj(clz:KClass<out Any>):Any{
       
        //2、有参的构造函数
        //创建对象 是通过构造函数来创建的
        //找到有参的构造函数 -> 创建对象
        val priCons = clz.primaryConstructor
        //调用构造函数
        val obj = priCons!!.call("老王")
    
        return obj
    }
    
    class Student2(var name:String){
        constructor():this("小王"){}
    
    }
    
    
    打印结果
    • 通过类名和函数名调用方法
    //调用函数
    fun  invokeFun(clz:KClass<out Any>,funName:String){
        //获取默认的主构造函数
        val priCons = clz.primaryConstructor
        //创建对象
        val obj = priCons?.call("老王")
    
        //查找这个函数是否存在
        clz.functions.find { it.name==funName }.also {
            //当调用类里面的方法时 必须将对象传递过去
            it!!.call(obj,"西大")
        }
    }
    
    打印结果
    • 通过Kclass和属性名访问属性
    class Student2(var name:String){
        constructor():this("小王"){}
        var age:Int = 20
        fun show(des:String){
            println("my name is $name des:$des")
        }
    
    }
    //调用属性 通过反射可以调用所有属性(不管是私有还是公开) 非常暴力
    fun invokeProperty(clz:KClass<out Any>,propName:String){
    
        //创建对象
        val primary = clz.primaryConstructor
        val obj = primary!!.call("Android大神")
    
        //查找age属性
        clz.memberProperties.find { it.name==propName }.also {
            //获取对象的get方法 call其实就是调用对象的get方法
            val value = it?.call(obj)
            println("获取属性${propName}的值:$value")
        }
    
    }
    
    打印结果
    class Student2(var name:String){
        constructor():this("小王"){}
        var age:Int = 20
        fun show(des:String){
            println("my name is $name des:$des")
        }
    
    }
    //调用属性 通过反射可以调用所有属性(不管是私有还是公开) 非常暴力
    fun invokeProperty(clz:KClass<out Any>,propName:String){
    
        //创建对象
        val primary = clz.primaryConstructor
        val obj = primary!!.call("Android大神")
    
        //查找age属性
        clz.memberProperties.find { it.name==propName }.also {
            //获取对象的get方法 call其实就是调用对象的get方法
    //        val value = it?.call(obj)
    //        println("获取属性${propName}的值:$value")
    
            //调用对象的set方法
            //将KProperty类型转化为KMutableProperty类型
            //KMutableProperty1表示函数只有一个参数
            //KMutableProperty1<T,R>
    
            if (it != null){
                //如果属性的修饰符为private 必须设置是否可以设置
                it?.isAccessible = true
                val mPro = it as KMutableProperty1<Any, Any>
                mPro.set(obj,10)
    
                val result = it.call(obj)
                println(result)
            }
    
        }
    
    }
    
    打印结果

    注解

    什么是注解?
    注解其实就是某个类或属性……身份的标识,本身没有任何作用,注解往往和反射联合使用。比如,在通过反射创建一个对象时,一般需要判断这个类有没有某个注解,有的话就能返回实例化的对象,否则无法创建对象。
    注解的种类

    • @Target:指定这个注解可用于哪些元素(类、函数、属性、表达式,等等)。
    • @Retention:指定这个注解的信息是否被保存到编译后的class文件中,以及在运行时是否可以通过反射访问它。
    • @Repeatable:允许在单个元素上多次使用同一个注解。
    • @MustBeDocumented:表示这个注解是公开API的一部分,在自动产生的API文档的类或者函数签名中,应该包含这个注解的信息。
    @Target(AnnotationTarget.CLASS)
    annotation class TableName
    
    @Target(AnnotationTarget.CONSTRUCTOR)
    annotation class MYCons
    
    @Target(AnnotationTarget.PROPERTY)
    annotation class MYParam
    
    @Target(AnnotationTarget.FUNCTION)
    annotation class MYFunc
    
    @Target(AnnotationTarget.VALUE_PARAMETER)
    annotation class MyFuncParam
    
    @TableName
    class Person @MYCons constructor(){
        @MYParam val name:String = "jack"
        @MYCons constructor(name:String):this(){
            
        }
        @MYFunc fun show(@MyFuncParam des:String){
            
        }
    }
    

    注解的生命周期

    SOURCE:处于代码编写时期
    BINARY:持续到字节码文件时期
    RUNTIME:整个运行阶段都存在


    生命周期的声明
    生命周期

    注:注解类中定义参数必须使用val,而不能使用var。

    注解类定义参数

    实战

    模拟Android中的Room创建表的实现原理
    读取Table(表)的信息,并创建对应的实例对象

    /**
     *@Description
     *@Author PC
     *@QQ 1578684787
     */
    class Student
    
    //模型数据
    @Entity
    data class User(
        @ColumnInfo  var id:Int,
        @ColumnInfo  var name:String,
        @ColumnInfo  var icon:String
    ){
        override fun toString(): String {
            return "User(id=$id,name='$name',icon='$icon')"
        }
    }
    
    //表明 -> 类名 一一对应
    //类注解
    //Retention默认值为AnnotationRetention.RUNTIME
    @Retention
    @Target(AnnotationTarget.CLASS)
    annotation class Entity
    
    //属性注解
    @Retention
    @Target(AnnotationTarget.PROPERTY)
    annotation class ColumnInfo
    
    //模拟数据的查询
    fun selectData():Map<String,Map<String,Any>>{
        //模拟有两个表User Student
        //使用Map封装数据
        val userData = mapOf(
            Pair("id",1),
            Pair("name","joke"),
            Pair("icon","www.google.com")
        )
        val studentData = mapOf(
            Pair("Sid",1),
            Pair("name","李王开"),
            Pair("address","西南大学")
        )
    
        return mapOf(
            Pair("User",userData),
            Pair("Student",studentData)
        )
    }
    
    
    fun autoParseFromTable(modelClz: KClass<out Any>):Any?{
        //先从数据库中读取出表的对应数据
        val datas = selectData()
        //判断传递过来的KClass对象有没有Entity注解
        val entity = modelClz.findAnnotation<Entity>()
    
        return if (entity==null){
            //传递过来的类没有Entity注解
            //不能进行转换
            null
        }else{
            //如果传递过来的类有Entity注解
            //就创建对象实例
            //获取表名
            val tableName = modelClz.simpleName
            val info = datas[tableName]
    
            //创建对象 将info的数据 填充到对象对应的属性中
            //使用默认的主构造函数创建
    
            val constructor = modelClz.primaryConstructor
            //创建一个数组保存解析的属性值
            //创建的数组元素个数 和 构造函数中参数的个数相同 初始值为null
            val params = arrayOfNulls<Any>(constructor!!.parameters.size)
    
            //遍历构造函数的参数
            constructor.parameters.forEach{
                //从数据源中获取这个参数对应的值
                val value = info?.get(it.name)
                //将这个值保存到数组中
                params[it.index] = value
            }
            //调用构造函数创建对象  
            //将一个数组传递给可变参数时 需在参数名前加*
            //vararg 对应的是Array类型的数组 不能是List
            constructor?.call(*params)
        }
    }
    
    fun main(){
        val user = autoParseFromTable(User::class) as User
        println(user.id)
    
    }
    

    相关文章

      网友评论

        本文标题:kotlin 注解和反射

        本文链接:https://www.haomeiwen.com/subject/batooltx.html