获取上面 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”
)
)
val copiedPerson = person.copy()
val deepCopiedPerson = person.deepCopy()
println(person === copiedPerson) //false
println(person === deepCopiedPerson) //false
println(person.group === copiedPerson.group) //true for shallow copy.
println(person.group === deepCopiedPerson.group) //false
println(deepCopiedPerson)
}
上面的例子中主要有几点需要注意的:
-
this::class.isData
判断是否是数据类 data class -
this::class.primaryConstructor
获取主构造器,因为是数据类一定有主构造器,所以可以强转!!
-
primaryConstructor.parameters
let里面调用当前primaryConstructor对象的parameters获取所有的构造器参数 -
this::class as KClass<T>
逆变转协变,否则this.class返回一个协变点out T, 而get()方法接受一个逆变点,会报错 -
memberProperties.first { it.name == parameter.name }
数据类的特点是构造器的参数名和成员的属性名相等 -
parameter.type.classifier as? KClass<*>
type参数需要调用classifier先转成KClass然后再判断是否是数据类 -
parameter to value?.deepCopy()
如果value是数据类继续调用value的deepCopy()方法深拷贝,这里是一个递归调用,K to V 是返回的一个 Pair对象 -
.toMap()
将Pair集合转Map集合 -
.let(primaryConstructor::callBy)
调用主构造器,callsBy是KCallable接口的方法,KFunction是KCallable的子类,因此所有的KFunction都可以调用callBy,callBy接受一个map参数正好就是let前面返回的结果。
这个例子有一个完整的开源库代码 KotlinDeepCopy 是由我参阅的学习资料的大神Bennyhuo所写的,但是貌似目前没什么issue, 慎用,可以当案例学习一下。
实例:实现 Model 映射
这个例子是实现一个拷贝工作,将一个对象里的字段赋值给另一个对象里面的同名字段,跟深拷贝的例子有点相似
//任意对象转其他对象(成员属性名相同)
inline fun <reified From : Any, reified To : Any> From.mapAs(): To {
//所有成员转成Map集合再调用下面的Map转对象方法即可
return From::class.memberProperties.map { it.name to it.get(this) }
.toMap().mapAs()
}
//Map转对象(成员属性名相同) 默认只处理数据类
inline fun Map<String, Any?>.mapAs(): To {
//primaryConstructor反射调用主构造器
return To::class.primaryConstructor!!.let {
it.parameters.map {
parameter ->
// this[parameter.name] 有可能为null, 如果目标对象To的构造器参数类型可以接受null类型就直接返回一个null 否则抛异常
parameter to (this[parameter.name] ?: if(parameter.type.isMarkedNullable) null
else throw IllegalArgumentException(“${parameter.name} is required but missing.”))
}.toMap()
.let(it::callBy)//callBy调用主构造器构造出一个To类型的对象
}
}
第一个方法的实现实际上是调用第二个方法的,所以只需实现第二个方法即可,这里依然是先获取主构造器To::class.primaryConstructor
,获取了主构造器之后拿到它的参数列表进行map操作,map里面依然是返回当前参数 parameter to value
,to 操作符左边的是To对象的也就是目标对象, to 操作符右边的是当前调用.mapAs的map对象,因此通过this[parameter.name]访问它里面的同名参数的value值,但是这个值可能为null, 不为null就返回 ?: 左边它自身,为null还需一个处理就是如果To类型即目标类的构造函数的这个当前参数可接受可空类型 ,就直接传null, 否则抛异常。
调用测试代码:
data class UserVO(val login: String, val avatarUrl: String)
data class UserDTO(
var id: Int,
var login: String,
var avatarUrl: String,
var url: String,
var htmlUrl: String
)
fun main() {
val userDTO = UserDTO(
0,
“world”,
“https://ccccccc”,
“https://ddddddddd”,
“https://eeeeeeeeeee”
)
val userVO: UserVO = userDTO.mapAs()
println(userVO)
val userMap = mapOf(
“id” to 0,
“login” to “hello”,
“avatarUrl” to “https://aaaaaaa”,
“url” to “https://bbbbbbbb”
)
val userVOFromMap: UserVO = userMap.mapAs()
println(userVOFromMap)
}
实例:可释放对象引用的不可空类型
这个例子主要是模仿了一个Android当中释放bitmap对象赋值为null的场景,在kotlin当中如果你定义了一个 var bitmap: Bitmap, 然后在onDestroy方法里面将其置为null, 但是这样写bitmap=null是不行的,因为定义的时候是一个不可空类型,这就矛盾了。
fun releasableNotNull() = ReleasableNotNull()
class ReleasableNotNull<T: Any>: ReadWriteProperty<Any, T> {
private var value: T? = null
override fun getValue(thisRef: Any, property: KProperty<*>): T {
return value ?: throw IllegalStateException(“Not initialized or released already.”)
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
this.value = value
}
fun isInitialized() = value != null
fun release() {
value = null
}
}
inline val KProperty0<*>.isInitialized: Boolean
get() {
//允许反射获取
isAccessible = true
//this.getDelegate()获取属性代理的实例
return (this.getDelegate() as? ReleasableNotNull<*>)?.isInitialized()
?: throw IllegalAccessException(“Delegate is not an instance of ReleasableNotNull or is null.”)
}
fun KProperty0<*>.release() {
isAccessible = true
(this.getDelegate() as? ReleasableNotNull<*>)?.release()
?: throw IllegalAccessException(“Delegate is not an instance of ReleasableNotNull or is null.”)
}
class Bitmap(val width: Int, val height: Int)
class Activity {
private var bitmap by releasableNotNull()
fun onCreate(){
//::bitmap省略了this, 默认绑定了当前对象为receiver
println(this::bitmap.isInitialized)
println(this::bitmap.isInitialized)
bitmap = Bitmap(1920, 1080)
println(::bitmap.isInitialized)
}
fun onDestroy(){
println(::bitmap.isInitialized)
::bitmap.release()
println(::bitmap.isInitialized)
}
}
fun main() {
val activity = Activity()
activity.onCreate()
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。
Android高级架构师之路很漫长,一起共勉吧!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
ndroid中高级的PDF技术文档。以及一些大厂面试真题解析文档。**
[外链图片转存中…(img-TUX7l6nf-1712142881969)]
Android高级架构师之路很漫长,一起共勉吧!
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算