android - 使用函数引用来注册和注销监听器是否安全?
问题描述
我最近发现自己在编写以下代码:
fun listener() {
// Do some stuff
adapter.removeLoadStateListener(::listener)
}
adapter.addLoadStateListener(::listener)
一位同事评论说,
val x1 = ::listener
val x2 = ::listener
x1 == x2 //true
x1 === x2 //false
然而,
var mySet = mutableSetOf<() -> Unit>()
fun a() { }
fun b() { }
mySet.add(::a)
mySet.add(::b)
mySet.remove(::a)
mySet.contains(::a) // false
mySet.contains(::b) // true
基于此,我得到了我无法::myFun
正确理解的印象,我开始质疑我的原始代码是否可以安全使用。
TL;博士
- 使用引用用作侦听器的方法是否安全,
::listener
并且需要多次引用(例如添加+删除)? - 关于匿名类等的幕后实际发生了什么?
编辑
最终我们决定不使用::listener
这种情况。但是,我们遇到了一个新问题,因为add/removeLoadStateListener
将只接受(CombinedLoadState) -> Unit
类型。
我将把我们对该问题的解决方案留在这里以供将来参考和其他相关读者参考(我希望有些人甚至可能会来这里寻找这个问题而不是原始问题的答案):
val listener = object : (CombinedLoadStates) -> Unit {
override operator fun invoke(loadState: CombinedLoadStates) {
// Do some stuff
adapter.removeLoadStateListener(this)
}
}
adapter.addLoadStateListener(listener)
解决方案
当您使用 时::listener
,您每次都在创建一个实现接口的匿名类。但是,正如您的测试所示,.equals
匿名类将返回它们相等,而匿名类(使用object:
语法创建)通常不会出现这种情况。因此,使用创建的两个实例::listener
将等同于 with==
但不等同于===
.
Sets通常使用.equals
相等(==
这打破了 Set 契约,但一个类可能出于某种原因在内部使用它。
使用它是否安全取决于您正在使用的类是否比较侦听器实例.equals
或身份。如果它可能使用 IdentityHashMap 来存储和比较侦听器,那么这是不安全的。
推荐阅读
- wordpress - 如何配置 WordPress 重定向
- php - 从 V(1) 与 V(any) 的属性匹配的所有顶点添加边
- c++ - 与 c 相比,为什么开发人员将 c/c++ 用于嵌入式系统而不是像 python 这样的高级语言?
- sql - 如何在 LEFT JOIN 中写“if else”
- javascript - Firestore“startAt”不断带来相同的数据
- angular - 在 Angular 中将 HTML 字符串显示为纯文本
- angular - 电话有时会在 Ionic 4 中跳过循环内的功能
- nodes - 为什么人们在某些情况下使用节点?
- javascript - 在 Mongoose 的 updateOne Post Hook 中获取文档 ID
- python - 带有二进制图像的 3D CNN 的 Keras?