“在构造函数中泄漏‘this’”警告应该适用于最终类和开放类吗?

Yon*_*bbs 20 kotlin

在 Kotlin 中,如果您有一个在其构造函数或块中open引用的类,您(非常正确)会收到编译器警告:thisinit

在非最终类的构造函数中泄漏“this”

原因在此处解释。

我的问题是:为什么在班级结束时报告这一点?如果this在该init块完成之前在该块中使用,则该对象仍未处于完全构造状态,所以警告不应该也适用于那里吗?

这甚至可能导致val属性似乎在运行时发生变化的情况。以这段代码为例:

class Listener {
    fun onCreated(leaker: Leaker) = println("Listener hears that leaker created with a value of ${leaker.myVal}")
}

class Leaker(listener: Listener) {
    val myVal: Int

    init {
        listener.onCreated(this)
        myVal = 1
        println("Leaker knows that it's been created with a value of $myVal")
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这些对象如下:

Leaker(Listener())
Run Code Online (Sandbox Code Playgroud)

将导致以下输出:

Listener hears that leaker created with a value of 0
Leaker knows that it's been created with a value of 1
Run Code Online (Sandbox Code Playgroud)

请注意,myVal最初报告为 0,然后为 1。

可以看出,Leaker将自身的实例传递给ListenerbeforeLeaker已完全构造。Listener然后可以在myVal初始化之前访问该属性,因此它将具有默认值(在这种情况下为 0,因为它是一个整数)。稍后Listener更改此属性的值(在本例中为 1)。这意味着程序的行为就像 aval发生了变化。

编译器应该警告你吗?

Rol*_*and 15

tl;博士: https : //youtrack.jetbrains.com/issue/KT-22044非常适合解决这个问题。

我将引用 Intellij IDEAs 检查中称为“在构造函数中泄漏‘这个’”的内容:

该检查报告了构造器内部的危险操作,包括:

  • 在构造函数中访问非最终属性
  • 在构造函数中调用非最终函数
  • 在非最终类的构造函数中使用this作为函数参数

这些操作很危险,因为您的类可以被继承,而此时派生类尚未初始化。典型例子:

abstract class Base {
    val code = calculate()
    abstract fun calculate(): Int
}

class Derived(private val x: Int) : Base() {
    override fun calculate() = x
}

fun testIt() {
    println(Derived(42).code) // Expected: 42, actual: 0
}
Run Code Online (Sandbox Code Playgroud)

我认为仍然应该有警告,因为您可以访问未初始化的变量。原因:编译器已经禁止直接访问未初始化的变量,即以下不会编译:

class Demo {
  val some : Int
  init {
    println(some) // Variable 'some' must be initialized
Run Code Online (Sandbox Code Playgroud)

但是间接访问它会编译并显示变量类型的默认值:

class Demo2 {
  val some : Int
  val someString : String
  init {
    fun Demo2.indirectSome() = some
    fun Demo2.indirectSomeString() = someString
    println(indirectSome()) // prints 0
    println(indirectSomeString()) // prints null; and yes.. this can lead to NullPointerExceptions
Run Code Online (Sandbox Code Playgroud)

在那里我们也有一个“泄漏”,基本上是some在它应该之前访问;-)