Kotlin反射全解析2 -- 超级好用的KClass
前言
我们继续来聊反射,或许对于Java开发者来说,Class很熟悉,不过不熟悉也没事,我们来看Kotlin中的反射类:KClass。
至于什么是反射可以查看前面一篇文章:
正文
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的使用了,我们下篇文章继续。