首页 > 解决方案 > 使用函数引用来注册和注销监听器是否安全?

问题描述

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

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;博士

  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)

标签: androidkotlin

解决方案


当您使用 时::listener,您每次都在创建一个实现接口的匿名类。但是,正如您的测试所示,.equals匿名类将返回它们相等,而匿名类(使用object:语法创建)通常不会出现这种情况。因此,使用创建的两个实例::listener将等同于 with==但不等同于===.

Sets通常使用.equals相等(==这打破了 Set 契约,但一个类可能出于某种原因在内部使用它。

使用它是否安全取决于您正在使用的类是否比较侦听器实例.equals或身份。如果它可能使用 IdentityHashMap 来存储和比较侦听器,那么这是不安全的。


推荐阅读