“在构造函数中泄漏'this'”警告应适用于最终类还是开放类?

时间:2018-12-20 10:31:05

标签: kotlin

在Kotlin中,如果您的open类在其构造函数或this块中引用了init,那么您(很正确)会收到编译器警告:

  

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

here对此进行了解释。

我的问题是:为什么在班级结业时没有未报告?如果在该块完成之前在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")
    }
}

按如下方式使用这些对象:

Leaker(Listener())

将产生以下输出:

Listener hears that leaker created with a value of 0
Leaker knows that it's been created with a value of 1

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

可以看出,LeakerListener完全构建之前将其实例传递给LeakerListener随后可以在初始化之前myVal属性的访问,因此它将具有默认值(在这种情况下为0,因为它是整数)。稍后在Listener上,将该属性的值更改为1(在本示例中为1)。这意味着程序的行为就像val已更改一样。

编译器是否应该对此发出警告?

1 个答案:

答案 0 :(得分:1)

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

我将引用Intellij IDEA检查中称为“在构造函数中泄漏'this'”的内容:

  

此检查报告了构造函数内部的危险操作,包括:

     
      
  • 在构造函数中访问非最终属性
  •   
  • 在构造函数中调用非最终函数
  •   
  • 使用 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
}

我认为仍然应该发出警告,因为您能够访问未初始化的变量。原因:编译器已经禁止直接访问未初始化的变量,即以下代码将无法编译:

class Demo {
  val some : Int
  init {
    println(some) // Variable 'some' must be initialized

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

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

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