Kotlin中使用Java数据类时引发的一个Bug

文章目录

基础复习:Kotlin语言中的对象比较

  • 比较对象的内容是否相等 (== 或者 equals ):Kotlin 中的操作符 == 和 equals效果相同 ,都用于比较对象的内容是否相等, Kotlin中建议直接使用 ==。
  • 比较对象的引用是否相等 ( === ):Kotlin 中的操作符 === 用于比较对象的引用是否指向同一个地址,运行时如果是基本数据类型 === 等价于 ==。

背景

如图效果,通过RecyclerView实现,对每个Item中前后数据对比来确定执行什么局部操作(如Item的insert、update、delete等),这里使用RecyclerView库中的DiffUtil.Callback()来进行的前后数据对比,如下:

kotlin 复制代码
class DataDiffUtil(private val oldModels: List<Any>, private val newModels: List<Any>) :DiffUtil.Callback() {

    /**
     * 旧数据
     */
    override fun getOldListSize(): Int = oldModels.size

    /**
     * 新数据
     */
    override fun getNewListSize(): Int = newModels.size

    /**
     * DiffUtil调用来决定两个对象是否代表相同的Item。true表示两个Item相同(表示View可以复用),false表示不相同(View不可以复用)
     * 例如,如果你的项目有唯一的id,这个方法应该检查它们的id是否相等。
     */
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldModels[oldItemPosition]::class.java == newModels[newItemPosition]::class.java
    }

    /**
     * 比较两个Item是否有相同的内容(用于判断Item的内容是否发生了改变),
     * 该方法只有当areItemsTheSame (int, int)返回true时才会被调用。
     */
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldModels[oldItemPosition] == newModels[newItemPosition]
    }

    /**
     * 该方法执行时机:areItemsTheSame(int, int)返回true 并且 areContentsTheSame(int, int)返回false
     * 该方法返回Item中的变化数据,用于只更新Item中变化数据对应的UI
     */
    override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
        return super.getChangePayload(oldItemPosition, newItemPosition)
    }
}

以顶部的Item1模块举例,当服务端有新数据来时,通过下面方式进行更新:

kotlin 复制代码
/**
 * use[DiffUtil] 增量更新数据
 * @param newList 新数据
 */
fun submitList(newList: MutableList<Any>) {
    //传入新旧数据进行比对
    val diffUtil = DataDiffUtil(mModels, newList)
    //经过比对得到差异结果
    val diffResult = DiffUtil.calculateDiff(diffUtil)
    //NOTE:注意这里要重新设置Adapter中的数据
    setModels(newList)
    //将数据传给adapter,最终通过adapter.notifyItemXXX更新数据
    diffResult.dispatchUpdatesTo(this)
}

如果Item1前后数据是一样的,那么areContentsTheSame() 中的oldModels[oldItemPosition] == newModels[newItemPosition] 理论上返回的就是true了。那么Item1模块也就不会再刷新了。

实际跑起来能按我们的预期走吗?

问题出现

上述逻辑写的差不多了,还差Model数据类没有写出来,因为项目中是Kotlin & Java混用的,而Model数据类正好是用Java语言编写的:

kotlin 复制代码
public class VP2Model implements Serializable {
    public int id;
    public String content;
}

看上去一切都是OK的,但是运行之后发现出问题了,即使前后数据完全一样,仍然会进行Item1的刷新,说明areContentsTheSame()里的数据对比返回的是false,通过断点发现确实返回了false。

到这里不知道大家有没有发现问题所在 ?开始我以为是数据变了,但是通过Log打点发现前后数据也是一样的,那么明明是一样的,为什么对比会是不同呢?仔细一想明白了,问题出在Java语言上,出在VP2Model类中没有重新equals()方法:

kotlin 复制代码
@Override
public boolean equals(@Nullable Object obj) {
    return super.equals(obj);
}

Java Model 类默认的equals()方法是比较的对象内存地址,刷新前后生成的显然不是同一个对象,那么前后内存地址对比返回的肯定是false了,问题就出在了这里!

如果我们使用 Kotlin 语言编写 Model 类就不会有这个问题,因为 Kotlin 编译器自动帮我们重写了equals()/hashCode()方法,如:

kotlin 复制代码
data class VP2Model(
    val id: Int = 0,
    val content: String = "",
)

注意这里要用data class开头才行,上述代码转换成Java后:

可以看到 Kotlin 编写的 Model 类自动帮我们实现了其中的equals()/hashCode()方法。

解决方式

已经知道问题出现的原因,那么解决方式就很简单了,比如下面几种解决方式:

方式一

重写Java Model 类中的 equals()方法,对每个字段进行对比,都相同返回 true,否则返回 false。

有一种快捷生成方式,在Mac版的 AS 中,可以使用 command + N 的方式生成,如下:

生成结果:

方式二

在使用的地方用 Kotlin语言编写Model 类进行转换,注意:这里一定要用data class 开头的声明,因为 Kotlin 编译器会自动帮我们重写 equals()/hashCode() 方法。

相关推荐
2601_962106641 小时前
【Java Set综合实战】商品管理系统 + 图书收藏系统|Product、Book自定义类全套代码
java
RuoyiOffice2 小时前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
2601_962106643 小时前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
传奇开心果编程3 小时前
【Jetpack Compose娓娓道来】第36课:焦点管理与键盘输入——让遥控器、键盘和输入法都“找得到北”
android·kotlin·移动开发·交互·android jetpack·ui框架·声明式ui
杨凯凡3 小时前
Spring Framework 7 API 版本管理实战:请求头路由、兼容迁移与废弃策略
java·rest api·springframework·spring boot 4·api版本管理
T_Wizard3 小时前
同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个
java·ai编程
hai_android4 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
红尘伴蝶舞4 小时前
从秒开优化到连环崩溃:深入剖析 ViewPager2 在常驻 DialogFragment 中的复用陷阱与自愈方案
android·kotlin
斯维赤4 小时前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
aiks_yuan7 小时前
Java CPU 100% 怎么排查:JDK 11 起 jstack 自带线程 CPU 时间,不用再 top -H 换算十六进制(JDK 8、11、17 实测)
java