利用KSP开发Kotlin的深拷贝库

前言
已经有很长一段时间没有见面了,在此期间因为参加比赛缘故没有办法去继续更新视频,不过这一切都将在今年的9月17日后结束,到时间再更新视频。
尽管我在这段时间没有更新视频,但是学习的脚步没有停止,在最近我学习了KSP的相关内容,虽然很浅,但是我还是想尝试一下,因此,一个Kotlin的对象深拷贝库——DeepReCopy就诞生了!
但是该库目前只是做出来了,只在我和另外一位贡献者中测试过,因此现在需要对该库有兴趣的开发者对该库进行尝试,随时愿意听取意见。
GitHub:https://github.com/1250422131/DeepReCopy
假设你已经了解深拷贝是什么了,那么请跳过下面,直接看这个库如何使用!
什么是深拷贝?
假设我们有一个Data类,就像是下面这样

设想一下它会发生什么?没错,你确实得到了一个新的DocInfo对象,它的内容与原来的对象一样,但是确实一个新的对象。
那么你认为UserInfo会是什么情况呢?

下面是输出结果,这里要提到一点的是,kotlin中的"=="是比较两个对象的值是否相同,而"==="则是比较引用地址是否相同。
结果实际上和我们预想的是一样的,但是我们发现其中的UserInfo却没有改变?
这就是典型的浅拷贝,也许这样还比较抽象,那么我们看看下面的图,为什么会造成这样的现象。

显然,我们发现浅拷贝只是单纯的把内存地址拷贝了过来,也就是说,虽然上面产生了新的DocInfo对象,但是新旧DocInfo中持有对象的内存地址是一致的。
但有时候我们需要拷贝后内部对象也是一个新对象,而不是只拷贝其中的内存地址。
例子:为了提高数据利用效率,我们会对比新旧两个数据是否发生了变化,但假设新的数据是拷贝旧的数据后改变了部分内容,由于没有深拷贝,改变新的数据,相当于改变了旧的数据,这样就会出现问题。
当然,除此之外还有列表拷贝,往往我们新的数组需要对旧的调整,假设不深拷贝,也会出现类似问题。
所以我想做一个深拷贝的库,就像是下面这样:

这样我们得到的新的对象,其内部的对象也是新的,就是这样DeepReCopy这个库就诞生了!
库的引入
DeepReCopy就是为了解决深拷贝问题而诞生的,它可以通过对一个类加特殊注解,来对其生成深拷贝的扩展函数。

让我们看看如何在Android项目中引入DeepReCopy!
首先在根项目的gradle脚本里添加ksp的插件

接下来在你项目模块的顶部引入KSP

最后我们加入依赖就可以使用DeepReCopy了

库的使用介绍
我们在导入了库之后就可以在需要被深拷贝的类上加@EnhancedData,但是注意,UserInfo也需要深拷贝,因此它需要注解@EnhancedData,不过不用担心,对于Kotlin自己的类我们会有其他办法进行深拷贝,你可以不用在意(前提是它有办法被深拷贝)。

OK,添加完成后现在还不可以使用,因为KSP是进行代码生成,因此需要执行build。

点击这个位置即可,旧版本也是这个图标,等build完成。
完成后这个类会有一个扩展方法,叫做deepCopy,这里接受一个lambda表达式,里边就可以像copy()方法一样改变对象的值。

猜测下这次会怎么样?
啊哈,我们发现UserInfo的对象被改变了,相当于产生了新的UserInfo,这样深拷贝就完成了!
DeepReCopy做了什么?
我们按住ctrl点击deepCopy方法,看看是什么?
事实上下面的代码就是通过KSP完成的,DeepReCopy通过识别类的元信息生成了下面的代码,我们调用的deepCopy就来自这里。

这里我们发现有两个deepReCopy,这是因为我想让它支持DSL写法,传入copyFunction的就是支持DSL写法的函数,刚刚我们调用的就是它啦。

目前DeepReCopy已经支持了List、Map、Set的深拷贝,对于其他没有被@EnhancedData注解的对象,我们会检查是否实现了序列化接口,假设没有则检查是否存在clone方法,假设都不具备那么对这个对象就不支持深拷贝了。
这个库的详细开发见:https://juejin.cn/post/7266368395787010102
文末
事实上做这个库最开始是我看了霍佬的KSP推广视频,视频内讲述的正是Data类深拷贝的问题,得到启发后我就试着实现了DeepReCopy这个库。
DeepReCopy目前需要大家的测试和反馈,如果觉得库还不错记得Star
GitHub:https://github.com/1250422131/DeepReCopy
