Kotlin学习手记——反射,开发8年的老Android才知道
/ kotlin反射库,大小2.5M左右没错,有2.5M,不过编译后的大小还能接受kotlin获取KClass通过两个冒号cls.java // 转成java的Classcls.java.kotlin // 再转回到kotlin的KClass// 获取定义在类中的属性(直接写在类当中的)KClass是不带泛型的类,typeOf能拿到具体的泛型实际类型:println(mapCls)// 输出cla
dependencies {
// kotlin反射库,大小2.5M左右
implementation “org.jetbrains.kotlin:kotlin-reflect:1.4.20”
}
没错,有2.5M,不过编译后的大小还能接受
kotlin获取KClass通过两个冒号
var cls: KClass = String::class
cls.java // 转成java的Class
cls.java.kotlin // 再转回到kotlin的KClass
// 获取定义在类中的属性(直接写在类当中的)
val property = cls.declaredMemberProperties.firstOrNull()
KClass是不带泛型的类,typeOf能拿到具体的泛型实际类型:
val mapCls = Map::class
println(mapCls) // 输出class kotlin.collections.Map
val mapType = typeOf<Map<String, Int>>() // 拿到 KType
mapType.arguments.forEach { // 拿到 KType 中的每个参数泛型的类型
println(it) // 输出 kotlin.String 和 kotlin.Int
}
拿到KClass之后可以通过KClass的方法获取各种其他属性了
一个简单的示例:
open class S(val sex : Boolean) {
fun superFun() {
}
}
class A(val name : String, sex: Boolean) : S(sex) {
fun String.hello(){
}
fun foo() {
}
var age : Int = 0;
}
fun A.test() {
}
上面的class A继承了一个类,并且类的内部有其他类定义的扩展方法,本身也定义了一个扩展方法。
KClass提供了很多方法获取类的属性和方法,但是有一些区别,方法比较多,可以看一下区别:
fun main() {
// 能得到:age, name, foo(), (String.)hello(), equals(), hashCode(), toString(), sex, superFun()
println(A::class.members) 获取所有的成员属性和方法,包括其他类的扩展方法,包括父类的方法,但不包括构造方法
// 能得到:foo(), (String.)hello(), equals(), hashCode(), toString(), superFun()
println(A::class.functions) 获取所有的方法, 包括扩展方法,包括父类的
// 能得到:age, name, sex
println(A::class.memberProperties) 获取所有的成员属性 非扩展, 包括父类
// 能得到:foo(), equals(), hashCode(), toString(), superFun()
println(A::class.memberFunctions) 获取所有的成员方法 非扩展, 包括父类
// 能得到:(String.)hello()
println(A::class.memberExtensionFunctions) 获取所有的扩展方法, 包括父类
// 能得到:[]
println(A::class.memberExtensionProperties) 获取所有的扩展属性, 包括父类
// 能得到:age, name
println(A::class.declaredMemberProperties) 获取到所有定义的属性 当前类
// 能得到:foo()
println(A::class.declaredMemberFunctions) 获取到所有定义的方法(普通方法,非扩展方法,非静态方法)当前类
// 能得到:age, name, foo(), (String.)hello()
println(A::class.declaredMembers) 获取到所有定义的成员包括属性和方法(普通方法和扩展方法)当前类
// 能得到:foo(), (String.)hello()
println(A::class.declaredFunctions) 获取到所有定义的方法(普通方法和扩展方法) 如果是java类可以获取父类的方法
// 能得到:(String.)hello()
println(A::class.declaredMemberExtensionFunctions) 获取定义在当前类的扩展方法
// 能得到:[]
println(A::class.declaredMemberExtensionProperties) 获取定义在当前类的扩展属性
}
可以看出以declared开头的方法基本上只能获取当前类的属性和方法,不带declared开头的方法则同时可以获取到父类的相关属性和方法。
还有一点需要注意的是,这里kotlin里面所指的扩展属性和扩展方法一般是指直接写在当前类中的其他类的扩展方法,如上面的A里面的String.hello()方法。如果是A类在某个地方定义的扩展方法是获取不到的,如上面的A.test()方法。这点跟java有点不一样。
nestedClasses获取内部类
B::class.nestedClasses//获取内部类
objectInstance获取object单例的实例,如果不是单例类则返回可能为null
B::class.objectInstance?.hello() //获取object实例
A::class.objectInstance?.foo() //如果类不是一个object, 则返回null
类内部的其他类如何获取外部类的实例对象:
class A {
fun String.hello(){
this 表示当前String对象
this@A 表示外部当前的class A对象
}
}
java也是一样,内部类获取外部类的实例时需要通过,A.this 获取
获取泛型实参:
1.获取接口某个方法的返回值类型的泛型参数
interface Api {
fun getUsers(): List
}
获取上面 Api 接口的 getUsers() 返回类型的泛型参数类 UserDTO
有几种方式,第一种是根据name来比较判断找到对应的方法:
//获取到 Api的getUsers() 方法 通过 filter
val functions = Api::class.declaredFunctions.filter { it.name == “getUsers” }
val getUsers : KFunction<*> = functions.get(0)
getUsers.returnType.arguments.forEach {
println(“getUser的返回值泛型:${it}”)
}
//获取函数的返回值参数的泛型类型UserDTO 通过 first
Api::class.declaredFunctions.first { it.name == “getUsers” }
.returnType.arguments.forEach {
println(“getUser的返回值泛型:${it}”)
}
还可以直接通过函数引用获取 Api::getUsers 得到的就是一个KFunction
Api::getUsers.returnType.arguments.forEach {
println(“getUser的返回值泛型2:${it}”)
}
显然这种方式最简单了。
还可以通过java的反射方式来获取:
//Api::class.java是获取到对应java的class Class 然后可以调用java的反射方法获取泛型类型
Api::class.java.getDeclaredMethod(“getUsers”)
.genericReturnType.safeAs()?.actualTypeArguments?.forEach {
println(it)
}
//safeAs是定义的一个Any的扩展方法
fun Any.safeAs(): T? {
return this as? T
}
//safeAs扩展方法可以简写下面的代码,等价于上面的代码
(Api::class.java.getDeclaredMethod(“getUsers”)
.genericReturnType as ParameterizedType).actualTypeArguments?.forEach {
println(it)
}
只能说java的方式也可以,但是这种也太麻烦了。。还是全部用kotlin的方法吧,不然得各种强转各种判空?.
2.获取接口类的泛型
abstract class SuperType {
//kotlin反射方法获取
val typeParameter by lazy {
//this是实际运行子类型, supertypes拿到父类型,first是第一个父类型即SuperType,arguments获取到泛型参数列表,只有一个可以first(),
// first()方法返回的是KTypeProjection,KTypeProjection.type才返回KType
this::class.supertypes.first().arguments.first().type!!
}
//java反射方法获取
val typeParameterJava by lazy {
this.javaClass.genericSuperclass.safeAs()!!.actualTypeArguments.first()
}
}
open class SubType : SuperType()
获取上面 SubType类实现的SuperType接口类的泛型:
val subType = SubType()
subType.typeParameter.let(::println) // kotlin.String
subType.typeParameterJava.let(::println) // class java.lang.String java获取的永远是java类型的描述
关键代码就是这句:this::class.supertypes.first().arguments.first().type 这里的话主要注意这个this运行时是实际的子类型(OO多态),所以最后是可以直接强转的。
上面代码是只有一个父类,如果有多个父类,会有问题,需要修改一下:
abstract class SuperType {
val typeParameter2 by lazy {
//实际中,如果子类是open可继承的可能还会有子类,具体要看使用的类
//此时需要找到合适的父类再操作,可以根据名字去比较,这里示例直接判断不是空的
this::class.allSupertypes.first { it.arguments.isNotEmpty() }.arguments.first().type!!
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。





既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!


由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
Android进阶资料
以下的资料是近年来,我和一些朋友面试收集整理了很多大厂的面试真题和资料,还有来自如阿里、小米、爱奇艺等一线大厂的大牛整理的架构进阶资料。希望可以帮助到大家。
Android进阶核心笔记

百万年薪必刷面试题

最全Android进阶学习视频
(img-CYPC6gJq-1711571906633)]
[外链图片转存中…(img-kbWluSMP-1711571906634)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-vcpew90e-1711571906634)]
Android进阶资料
以下的资料是近年来,我和一些朋友面试收集整理了很多大厂的面试真题和资料,还有来自如阿里、小米、爱奇艺等一线大厂的大牛整理的架构进阶资料。希望可以帮助到大家。
Android进阶核心笔记
[外链图片转存中…(img-Qvi1Tw66-1711571906635)]
百万年薪必刷面试题
[外链图片转存中…(img-l1zwEVQG-1711571906636)]
最全Android进阶学习视频
更多推荐



所有评论(0)