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> .

Зачем?

Как вы сказали в комментариях, parseResponse(...) вызывается из конструктора EnvelopeParser .

Тогда что происходит, когда вы создаете экземпляр ReadCursorRequestParser :

  1. Выделяется объект.

  2. ReadCursorRequestParser конструктор ReadCursorRequestParser , который сразу вызывает конструктор суперкласса.

  3. parseResponse(...) EnvelopeParser ) вызывает parseResponse(...) и, таким образом, назначает contacts (и в этот момент это фактически непустой список).

  4. ReadCursorRequestParser затем возвращается, и конструктор ReadCursorRequestParser продолжается.

  5. Конструктор ReadCursorRequestParser снова назначает contacts , теперь это пустой список .

Причиной этого является то, что каждый конструктор сначала вызывает свой супер-конструктор (если он есть) и только затем инициализирует свойства и выполняет блоки init , и все изменения, которые суперконструктор, сделанный для состояния, объявленного в классе (а не базовых классах), будут быть перезаписан собственным конструктором класса.

Этот упрощенный пример показывает это поведение: (link) .


Самое легкое обходное решение – изменить объявление contacts на что-то вроде

 lateinit var contacts: List<AddressBookElementParser> 

С помощью этого объявления конструктор не переназначает contacts .

Но я настоятельно рекомендую вам избегать вызова open функций в конструкторе, потому что, если они переопределены, они могут (и обычно) зависеть от состояния производного класса, которое еще не было инициализировано , а также изменения, которые они делают, будут перезаписывается конструктором производного класса. Вы даже можете закончить с некоторой частью изменений, которые сохраняются, потому что они сделаны для состояния суперкласса, а другая часть стерта – определенно не то, что вы хотите видеть в повседневной жизни.

Intereting Posts
android – Запуск приложения с ошибкой «com.android.builder.dexing.DexArchiveMergerException: невозможно слить dex ' Получить пару цифр из списка номеров в Котлин Как добавить весенние ресурсы в классы Apache Ignite? Использование Glide для кэширования изображений веб-просмотра Как заставить вызовы некоторым конструкторам / функциям использовать именованные аргументы? Котлин: назначается «это»? Apache Tomcat 8.0 не может загрузить класс сервлета, написанный в Kotlin, используя Eclipse для Java EE, Mars 2 Выполнить функцию из строки? AndroidAnnotations – ViewById не может использоваться для частного элемента ImageButton OnClickListener не работает Как сделать groupBy и собирать с помощью RxJava и Kotlin? В Android Java / Kotlin реализация интерфейса дает ему прямой доступ к локальным переменным класса вложенности Могу ли я использовать Coroutines Котлина, используя их в Java-коде? Kotlin Упрощение булевых выражений Почему IntelliJ Idea не находит зависимости Maven от другого модуля Kotlin?