在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。
可以看出,Leaker
在Listener
完全构建之前将其实例传递给Leaker
。 Listener
随后可以在初始化之前myVal
属性的访问,因此它将具有默认值(在这种情况下为0,因为它是整数)。稍后在Listener
上,将该属性的值更改为1(在本示例中为1)。这意味着程序的行为就像val
已更改一样。
编译器是否应该对此发出警告?
答案 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
之前应该访问它;-)