Kotlin:从自身/内部引用匿名对象(通过 this)

Kotlin: Referencing anonymous object from itself / inside (via this)

TL;DR 这些 object : someClass{ } 匿名对象无法通过 this 访问自身(这导致 外部对象 )。 如何访问它?

更长的解释:

对于我的 Fragment,我需要一个 PreDrawListener。我在 onCreateView 中调用它。执行时,我想事后删除侦听器。所以 Java 的做法会建议这样的事情

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,    

  val treeObserver = layout.viewTreeObserver

  treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
     override fun onPreDraw(): Boolean {
        layout.viewTreeObserver.removeOnPreDrawListener(this)
        ...
     }
  }

问题是,当查看 removeOnPreDrawListener(this) 时,this 对象不是侦听器而是 myFragment$onCreateView@f019bf0

或者,我可以直接访问 this@MyFragment,其中 returns 是对片段的引用。

不过,none 这些选项似乎是我的 PreDrawListener如何从内部访问它(如果有的话)?

老实说,我不明白你的问题。

this里面的anonymous指的是class本身,但是他们从来没有名字。您不能创建具有名称的匿名 class。为了演示这个,我写了一些示例代码:

class TheClass{
    fun run(){
        val anon = object: Runnable {
            override fun run() {}
        }
        println(anon::class.java.simpleName)
        println(anon::class.java.name)
    }
}

打印:

run$anon
com.package.TheClass$run$anon

现在,这很好,但它看起来仍然不像你的。但是你看到它匹配包含 class、方法、变量,最后是美元符号,表示它是一个匿名内部 class。这适用于第二个,也就是完整的那个。第一个只打印短名称,即方法、var 名称和显示它是匿名函数的美元符号。

如果您对为什么会出现带有数字的美元符号感兴趣,请参阅 this。 T

让我们扩展它并放弃变量。显然,这是糟糕的代码(而且内存效率很低,但它是一个演示所以没关系):

class TheClass {
    fun run(){
        println(object: Runnable {
            override fun run() { }
        })
    }
}

这将打印并匹配您的模式:

com.package.TheClass$run$anon

你已经看到了规律;现在你可以开始 "decoding" 你得到的散列:

myFragment // inside myFragment
$onCreateView // Inside a function
 // There is an anonymous class with a specific identifier
@f019bf0 // This is standard everywhere; just look up Object.toString()

我刚才试图证明的是:this 确实引用了您创建的匿名函数。匿名函数是匿名的。他们没有名字。他们使用 $number 作为标识符。所以如果你有这个代码:

treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
    override fun onPreDraw(): Boolean {
       layout.viewTreeObserver.removeOnPreDrawListener(this)
       ...
    }
 }

this 将引用侦听器,即使打印 class 可能会打印看起来令人困惑的内容。如果有什么东西坏了,那不是因为 this 没有提到听众(因为它确实如此)

此外,您的代码编译正常。那里也没有类型不匹配。如果它引用不同的对象,如果您将 this 传递给需要 OnPreDrawListener.

的方法,它将不起作用

使用 Java 中的相同代码,您会得到不同的结果。这是因为 Kotlin 将匿名函数编译为 Class$function$number,而 Java 将其编译为 Class$number。如果它在嵌套的 class 中,它将在 Kotlin 中显示为 Outer$Inner$function$number,在 Java.

中显示为 Outer$Inner$number

编译器的不同导致了不同的名称; Java 排除函数,而 Kotlin 包含它。它在 .class 文件名中,因此如果您构建项目并在文件资源管理器中查看文件名以查找您拥有的任何 OS(不要查看IntelliJ。它会为你反编译文件。请记住,你只是在寻找名称,IntelliJ 通过将 .class 文件合并为一个文件而搞砸了以匹配原始来源)


就像最终的元数据一样,我打印 class 而不是打印对象。接口没有重写的 toString 方法,这意味着它默认为 Object 上的方法,即 returns getClass().getName() + "@" + Integer.toHexString(hashCode());(可以找到原始代码 here)。 println(this)println(this.toString())一样,调用Object中的toString方法,打印class名字。 println(this) 与打印对象或打印 class

相同