Kotlin:超级构造函数中所做的更改将被覆盖

时间:2017-01-10 11:45:47

标签: kotlin

我无法理解Kotlin实际上在做什么:

我的单元测试如下:

@Test
fun testReadCursorRequest() {
    val xml = fromFile()
    val parser: ReadCursorRequestParser = ReadCursorRequestParser(xml)
    assertEquals(0, parser.status)
    assertEquals(134, parser.contacts!!.size)
}

我的解析器看起来像这样

abstract class EnvelopeParser(val xml: String) {
    abstract fun parseResponse(response: Element)

    init {
        parseResponse(xmlFromString(xml))
    }

    // non-related stuff
}

class ReadCursorRequestParser(xml: String) : EnvelopeParser(xml) {

    var contacts: List<AddressBookElementParser> = mutableListOf()     

    override fun parseResponse(response: Element) {
        // here some parsing stuff, fills the contacts-list
        println("size is: ${contacts.size}")
    }
}

println说size is: 134,单元测试说:java.lang.AssertionError: Expected <134>, actual <0>

为什么?

1 个答案:

答案 0 :(得分:5)

正如您在评论中所说,parseResponse(...)是从EnvelopeParser构造函数内部调用的。

然后,当您创建ReadCursorRequestParser的实例时,发生了什么:

  1. 分配了一个对象。

  2. 调用ReadCursorRequestParser构造函数,它会立即调用超类构造函数。

  3. 超级构造函数(EnvelopeParser的)会调用parseResponse(...),从而分配contacts(此时这实际上是一个非空列表)。

  4. 然后超级构造函数返回,ReadCursorRequestParser的构造函数继续。

  5. ReadCursorRequestParser构造函数再次指定contacts,现在它是一个空列表

  6. 原因是每个构造函数首先调用其超级构造函数(如果有的话),然后才初始化属性并执行init块,以及超级构造函数对类中声明的状态所做的所有更改(不是基类)将被班级覆盖。自己的构造函数。

    此简化示例显示了此行为:(link)

    最简单的解决方法是将contacts的声明更改为

    lateinit var contacts: List<AddressBookElementParser>
    

    使用此声明,构造函数不会重新分配contacts

    但是我强烈建议你不要在构造函数中调用open函数,因为如果被覆盖,它们可能(并且通常会)依赖于派生类状态,而不是已初始化,派生类构造函数也会覆盖所做的更改。你甚至可以最终保留一些部分变更,因为它们是在超类状态下完成而另一部分被删除 - 绝对不是你想在日常生活中看到的。