使用函数引用注册和取消注册监听器是否安全?

3

我最近发现自己在编写以下代码:

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;DR:
1.使用 "::listener" 来引用需要被多次引用的方法(例如添加 + 删除)作为监听器是否安全? 2.关于匿名类等实际上正在发生什么?
编辑:
最终,我们决定不在此情况下使用 "::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)

问题是,你想做什么?如果你想找到一个监听器并删除该特定的监听器,那么你不能使用函数,因为每个函数调用都将是一个新实例(根据你自己的信息,我还没有测试)。为了解决这个问题,使用接口。如果你想删除所有的监听器,那么只需在适配器上清除监听器集合即可。在Android API中,有些类只支持1个监听器,通常是setListener,而其他类则是addListener - cutiko
因为每次函数调用都会产生一个新的实例。问题在于这如何影响函数引用(即不是函数_调用_)。请参见我使用集合的示例,其中 ::a 条目实际上被删除了,尽管变量示例表明 ::a !== ::a - Felix ZY
1个回答

2
当您使用::listener时,实际上是每次创建一个实现接口的匿名类。然而,正如您的测试所示,匿名类的.equals会返回它们相等,这通常不是匿名类(使用object:语法创建)的情况。因此,使用::listener创建的两个实例将等价于==但不等于===
集合通常使用.equals相等性(==)来确定实例是否重复,但可能会创建一个由IdentityHashMap支持的集合,因此它有效地行为类似于使用身份比较的集合。这违反了集合契约,但某个类可能在内部出于某些原因使用它。
是否安全使用此功能取决于您正在处理的类是否通过.equals或标识来比较侦听器实例。如果可能正在使用IdentityHashMap存储和比较侦听器,则不安全。

能否详细说明一下“创建匿名类”的具体方法? - Felix ZY
1
没有什么特别的要说,这就是为什么我用了“有点”的原因。它是将函数包装在实现接口的匿名类中。 - Tenfour04
好的,我只是想确认一下这是否属实。 “有点”听起来好像还有其他事情正在发生。 - Felix ZY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接