2024年安卓最全Kotlin学习手记——反射,面试高频算法

文末

面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责!

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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!!

//等价上面

//this::class.allSupertypes.filter{ it.arguments.isNotEmpty()}.first().arguments.first().type!!

}

}

open class SubType : SuperType()

class SubType2: SubType()

获取上面 SubType2类的父类实现的SuperType接口类的泛型:

val subType2 = SubType2()

subType2.typeParameter2.let(::println) // kotlin.String

实例:为数据类实现 DeepCopy

fun T.deepCopy(): T {

//是数据类data class 才拷贝

if(!this::class.isData){

return this

}

//primaryConstructor获取主构造器,因为执行到这里的是数据类肯定有主构造器,所以!!强转,不用判空

return this::class.primaryConstructor!!.let {

primaryConstructor ->

primaryConstructor.parameters.map { parameter ->

//(this::class as KClass)逆变转协变

val value = (this::class as KClass).memberProperties.first { it.name == parameter.name } //成员属性名和构造函数的参数名相等

.get(this)

//classifier先转成KClass,然后判断是否是数据类

if((parameter.type.classifier as? KClass<*>)?.isData == true){

parameter to value?.deepCopy() //如果value是数据类继续调用value的deepCopy()方法深拷贝 递归

} else {

parameter to value // 如果value不是数据类直接返回,(K to V)是返回一个Pair对象

}

}.toMap() //Pair集合转Map集合

.let(primaryConstructor::callBy) //callBy调用构造函数构造对象, callBy需要一个Map<KParameter, Any?>参数就是当前的map对象

}

}

调用测试代码:

data class Person(val id: Int, val name: String, val group: Group)

data class Group(val id: Int, val name: String, val location: String)

fun main() {

val person = Person(

0,

“hello”,

Group(

0,

“Kotliner.cn”,

“China”

)

)

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值