美文网首页kotlin
Kotlin反射全解析2 -- 超级好用的KClass

Kotlin反射全解析2 -- 超级好用的KClass

作者: 蜗牛是不是牛 | 来源:发表于2022-04-22 22:16 被阅读0次

    前言

    我们继续来聊反射,或许对于Java开发者来说,Class很熟悉,不过不熟悉也没事,我们来看Kotlin中的反射类:KClass。

    至于什么是反射可以查看前面一篇文章:

    Kotlin反射全解析1 -- 基础概念

    正文

    KClass其实就是Class的Kotlin版本,不过我个人觉得它的API设计的更好。

    KClass的概述和获取

    这个KClass就是Kotlin反射的主要类,我们还是先看一下源码注释:

    //KClass的注释
    Represents a class and provides introspection capabilities. Instances of this class are obtainable by the ::class syntax. See the Kotlin language documentation  for more information.
    Params:
    T - the type of the class.
    
    
    • 表示一个类具有内省功能,然后该类的实例可以通过 ::class 语法获取。

    我觉得这里的内省说的非常好,也就是通过KClass能获取这个类的信息。

    我们直接来看个例子:

    //直接定义一个类
    class MainActivity : AppCompatActivity() {
        //私有成员变量
        private val privateInt = 10
        //成员变量
        val norInt = 10
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            testFunction()
        }
    
        fun testFunction() {
            //获取类的KClass
            val kClass = MainActivity::class
            //获取成员信息进行打印
            kClass.memberProperties.forEach {
                println("memberProperties $it")
            }
        }
    }
    
    

    然后会发现这个打印:

    2022-01-22 15:17:47.127 7832-7832/com.wayeal.testgit I/System.out: memberProperties val com.wayeal.testgit.MainActivity.norInt: kotlin.Int
    2022-01-22 15:17:47.128 7832-7832/com.wayeal.testgit I/System.out: memberProperties val com.wayeal.testgit.MainActivity.privateInt: kotlin.Int
    
    

    会发现能打印出MainActivity类的2个属性。

    既然这里说到了memeberProperties方法,我们来看一下这是个啥:

    val <T : Any> KClass<T>.memberProperties: Collection<KProperty1<T, *>>
        get() = (this as KClassImpl<T>).data().allNonStaticMembers.filter { 
            it.isNotExtension && it is KProperty1<*, *> } as Collection<KProperty1<T, *>>
    
    

    从这里会发现它是KClass的扩展函数,同时进行了过滤,当成员不是扩展的且是KProperty1类型的才可以,看到这里你或许又对了这个KProperty有点疑惑,不慌,后面我们都进仔细说说。

    KClass结构

    既然了解了反射就是获取类的信息,而Kotlin又把这些信息保存到KClass,所以必须要熟悉KClass结构,直接看一下其源码:

    //一共实现了3个接口,接口后面分析
    public actual interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier {
        //简单名字
        public actual val simpleName: String?
        //类的全名
        public actual val qualifiedName: String?
        //类以及父类定义的所有属性和方法
        //其中类型是KCallable 后面分析
        override val members: Collection<KCallable<*>>
        //所有构造函数
        //类型KFunction 后面分析
        public val constructors: Collection<KFunction<T>>
        //内部定义的所有类,包括内部类和静态嵌套类
        public val nestedClasses: Collection<KClass<*>>
        //该类的类型参数,即泛型类的类型参数
        public val typeParameters: List<KTypeParameter>
        //该类直接的父类类型列表
        public val supertypes: List<KType>
        //假如这个类是密封类,获取其所有子类
        public val sealedSubclasses: List<KClass<out T>>
        //该类的可见修饰符,也就是PUBLIC PROTECT等4种情况
        public val visibility: KVisibility?
        //是否是final,Kotlin的类默认就是final,无法继承
        public val isFinal: Boolean
        //是否是Open,和isFinal反过来
        public val isOpen: Boolean
        //是否是抽象的类
        public val isAbstract: Boolean
        //是否是密封类
        @SinceKotlin("1.1")
        public val isSealed: Boolean
        //是否是数据类
        public val isData: Boolean
        //是否是内部类
        public val isInner: Boolean
        //是否是伴生对象
        public val isCompanion: Boolean
        //类是否是一个Kotlin函数接口
        public val isFun: Boolean
        //是否是value class,这个是1.5才推出的新内容
        public val isValue: Boolean
    }
    
    

    这里的KClass接口加了很多Kotlin才有的方法,比如是否是某种class这种接口。

    其实这些方法大可不必去死记硬背,你可以想想你平时定义一个类的话,它包含哪些内容,我个人分析可以分为下面几个方面:

    • 类名,这个对应有对应的API去获取简易类名和全类名。

    • 可见修饰符,不外乎就是public、protected、internal和private。

    • 是否是open修饰即是否可以被继承,Kotlin默认是final的。

    • 如果是泛型类的话,它的类型参数。

    • 它继承或者实现的父类。

    • 它的内部类或者嵌套类。

    除了上面一些,还有以下重点:

    • 这个类的属性和方法,Kotlin这里把属性和方法都看成是KCallable的子类,所以用members统一返回,关于这个类,我们后面细说。

    • 构造方法,这里单独把构造方法给提出来,因为它是比较特殊的方法。

    KClass扩展函数

    其实KClass的API设计的还是非常好理解的,但是可能还是不太方便,举个很简单的例子,上面的KClass中members会返回父类和当前类的所有方法和属性,这就不太好,假如我只想获取当前类的方法或者属性呢 熟悉Java的同学可能知道,在Java中有不一样的API来区分这2种情况,在Kotlin中,直接就是又设计了一些扩展函数来区分,我们也来梳理一下,加深个印象,以防止平时开发自己又去实现一遍。

    //KClass的扩展函数
    
    //返回类的主构造函数,没有主构造函数返回null
    val <T : Any> KClass<T>.primaryConstructor: KFunction<T>?
        get() = (this as KClassImpl<T>).constructors.firstOrNull {
            ((it as KFunctionImpl).descriptor as ConstructorDescriptor).isPrimary
        }
    
    //返回伴生对象实例,没有的话返回null
    val KClass<*>.companionObject: KClass<*>?
        get() = nestedClasses.firstOrNull {
            (it as KClassImpl<*>).descriptor.isCompanionObject
        }
    
    //返回伴生对象实例,否则为null
    val KClass<*>.companionObjectInstance: Any?
        get() = companionObject?.objectInstance
    
    //返回该类定义的属性和方法,父类中的不计入
    val KClass<*>.declaredMembers: Collection<KCallable<*>>
        get() = (this as KClassImpl).data().declaredMembers
    
    //返回该类以及父类的所有函数,包括静态函数
    val KClass<*>.functions: Collection<KFunction<*>>
        get() = members.filterIsInstance<KFunction<*>>()
    
    //返回该类中的静态函数
    val KClass<*>.staticFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().allStaticMembers.filterIsInstance<KFunction<*>>()
    
    //返回该类和父类的所有成员函数,即非扩展、非静态的函数
    val KClass<*>.memberFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().allNonStaticMembers.filter { it.isNotExtension && it is KFunction<*> } as Collection<KFunction<*>>
    
    //返回该类和父类所有的扩展函数
    val KClass<*>.memberExtensionFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().allNonStaticMembers.filter { it.isExtension && it is KFunction<*> } as Collection<KFunction<*>>
    
    //返回该类的所有函数
    val KClass<*>.declaredFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().declaredMembers.filterIsInstance<KFunction<*>>()
    
    //返回该类中的非静态、非扩展函数
    val KClass<*>.declaredMemberFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().declaredNonStaticMembers.filter { it.isNotExtension && it is KFunction<*> } as Collection<KFunction<*>>
    
    //返回该类的扩展函数
    val KClass<*>.declaredMemberExtensionFunctions: Collection<KFunction<*>>
        get() = (this as KClassImpl).data().declaredNonStaticMembers.filter { it.isExtension && it is KFunction<*> } as Collection<KFunction<*>>
    
    //返回该类的静态属性
    val KClass<*>.staticProperties: Collection<KProperty0<*>>
        get() = (this as KClassImpl).data().allStaticMembers.filter { it.isNotExtension && it is KProperty0<*> } as Collection<KProperty0<*>>
    
    //返回该类和父类的所有非扩展属性
    val <T : Any> KClass<T>.memberProperties: Collection<KProperty1<T, *>>
        get() = (this as KClassImpl<T>).data().allNonStaticMembers.filter { it.isNotExtension && it is KProperty1<*, *> } as Collection<KProperty1<T, *>>
    
    //返回该类和父类的扩展属性
    val <T : Any> KClass<T>.memberExtensionProperties: Collection<KProperty2<T, *, *>>
        get() = (this as KClassImpl<T>).data().allNonStaticMembers.filter { it.isExtension && it is KProperty2<*, *, *> } as Collection<KProperty2<T, *, *>>
    
    //返回该类中的非扩展属性
    val <T : Any> KClass<T>.declaredMemberProperties: Collection<KProperty1<T, *>>
        get() = (this as KClassImpl<T>).data().declaredNonStaticMembers.filter { it.isNotExtension && it is KProperty1<*, *> } as Collection<KProperty1<T, *>>
    
    //返回该类的扩展属性
    val <T : Any> KClass<T>.declaredMemberExtensionProperties: Collection<KProperty2<T, *, *>>
        get() = (this as KClassImpl<T>).data().declaredNonStaticMembers.filter { it.isExtension && it is KProperty2<*, *, *> } as Collection<KProperty2<T, *, *>>
    
    //创建实例,通过空参数构造函数或者全参构造函数
    @SinceKotlin("1.1")
    fun <T : Any> KClass<T>.createInstance(): T {
        // TODO: throw a meaningful exception
        val noArgsConstructor = constructors.singleOrNull { it.parameters.all(KParameter::isOptional) }
                                ?: throw IllegalArgumentException("Class should have a single no-arg constructor: $this")
    
        return noArgsConstructor.callBy(emptyMap())
    }
    
    

    上面这些方法我个人觉得比Java中的API设计的更好一点,其实也是非常好理解和记忆的,比如方法分为是该类的还是包含其父类的,仅该类的可以用declared来命名其函数名,比如方法是否是扩展的,是扩展的话用extensions来命名其函数名,还有就是函数用Function,而属性用Properties来进行区分。

    总结

    其实看了这个KClass后是不是有一种豁然开朗的感觉,我们平时写的类,其信息都可以在这个KClass来获取,当然这里只是个简单概况,比如我想获取其中的属性或者方法,那就是KFunction和KProperty的使用了,我们下篇文章继续。

    相关文章

      网友评论

        本文标题:Kotlin反射全解析2 -- 超级好用的KClass

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