kotlin

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

2022-04-22  本文已影响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.

我觉得这里的内省说的非常好,也就是通过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这种接口。

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

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

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的使用了,我们下篇文章继续。

上一篇下一篇

猜你喜欢

热点阅读