Intereting Posts
Поток Akka, выше по потоку, не производя значения notifyDataSetChanged () не вызывает метод onBindViewHolder () Панель «Призрачная» нижняя панель навигации, когда фрагмент загружается в домашней деятельности В чем разница между запуском / соединением и асинхронным / ожиданием в сопрограммах Kotlin перейти к объявлению в приложении create-react-kotlin-app не работает когда использовать lateinit, init block и companion object. Котлин Как хранить временную переменную при инициализации объекта Kotlin? Реактор switchifempty не ведет себя так, как ожидалось, в тесте junit Как отобразить уведомление в приложении, которое не хранится в ящике уведомлений à la Snapchat Kotlin – Идиоматический способ проверки массива содержит значение Анимация не работает при сохранении в базе данных? Синтаксис Kotlin для наблюдателя LiveData? Странная ситуация возникает при сортировке слияния в Котлине ObjectAnimator.ofFloat не может принимать Int как параметры непосредственно в kotlin XML в / из Java / Kotlin, кросс-платформенный

Kotlin переопределяет абстрактное поведение val, объект vs class

Я только начал использовать и начал возиться с абстрактными классами, переопределять val и singeltons. Но, я только что столкнулся с действительно странным поведением. Моя цель состояла в том, чтобы иметь абстрактный класс, а затем создать несколько singeltons, которые расширяют этот абстрактный класс. Так как я хочу, чтобы некоторые переменные я создал абстрактные значения val, которые затем могут быть переопределены в подклассах (вместо передачи их через конструктор).

Итак, я получил 4 класса:

Основная деятельность:

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val instance = Instance() Log.d("MainActivity", "instance randObject: ${instance.randObject}") Log.d("MainActivity", "instance randObject: ${instance.randObject.myProp}") Log.d("MainActivity", "instance randObject: ${instance.randObject.myProp2}") Log.d("MainActivity", "singleton randObject: ${Object.randObject}") Log.d("MainActivity", "singleton randObject: ${Object.randObject.myProp}") Log.d("MainActivity", "singleton randObject: ${Object.randObject.myProp2}") } } 

Пример:

 class Instance: AClass(){ override val testString: String = "test" override val testUriString: String = "https://www.google.se" override val testUri: Uri = Uri.parse(testUriString)!! override val randObject: RandomObject = RandomObject("Herp") } 

объект

 object Object : AClass(){ override val testString: String = "test" override val testUriString: String = "https://www.google.se" override val testUri: Uri = Uri.parse(testUriString)!! override val randObject: RandomObject = RandomObject("Herp") } 

Класс:

 abstract class AClass{ abstract val testString: String abstract val testUriString: String abstract val testUri: Uri abstract val randObject: RandomObject init { Log.d("AClass", "testString: $testString") Log.d("AClass", "testUriString: $testUriString") Log.d("AClass", "testUri: $testUri") Log.d("AClass", "randObject: $randObject") } } 

Вывод:

 D/AClass: testString: null D/AClass: testUriString: null D/AClass: testUri: null D/AClass: randObject: null D/MainActivity: instance randObject: com.technocreatives.abstracttest.RandomObject@4455b26 D/MainActivity: instance randObject: derp D/MainActivity: instance randObject: Herp D/AClass: testString: test D/AClass: testUriString: https://www.google.se D/AClass: testUri: null D/AClass: randObject: null D/MainActivity: singleton randObject: com.technocreatives.abstracttest.RandomObject@8b19367 D/MainActivity: singleton randObject: derp D/MainActivity: singleton randObject: Herp 

После этого я пришел к осознанию того, что переопределенные, вероятно, не инициализируются до тех пор, пока не будет выполнен init{} . Но потом я увидел, что произошло, когда я создал синглтон. Значение testUriString устанавливается в init . Почему это так? Это ошибка? Каково ожидаемое поведение синглтона и переопределения val?

Я попытался найти документацию, но не нашел никакой информации об этом в документации.

Различие в поведении, которое вы наблюдаете, вызвано тем, как генерируются поля поддержки для свойств в классах и объектах, а также как они инициализируются.

  • Когда класс переопределяет свойство, используя поле поддержки, под капотом в производном классе есть отдельное поле экземпляра, а переопределенный getter возвращает значение этого поля.

    Таким образом, когда вы получаете доступ к свойству внутри конструктора суперкласса, это вызванный переопределенный getter, который возвращает null значение поля (в этот момент он не инициализируется, поскольку суперструктор вызывается перед инициализацией класса логика).

  • Напротив, когда вы определяете object переопределяющий класс, тогда базовый класс Object имеет свои поля поддержки, определенные как static поля JVM.

    Класс Object имеет экземпляр (его можно даже получить в Java как Object.INSTANCE ), и этот экземпляр инициализируется в определенный момент времени и вызывает супер-конструктор.

    Теперь интересная часть: когда класс класса класса загружается JVM, его статические поля, которые инициализируются постоянными значениями, уже содержат эти значения, даже до PUTSTATIC инструкции PUTSTATIC в <clinit> .

    Если вы измените инициализатор testString на непостоянное значение, он не будет инициализирован к моменту его обращения, например override val testString: String = "test".also { println(it) }

    Вот суть с байт-кодом такого сингла с несколькими марками, сделанными мной. Обратите внимание, что к полю обращается конструктор абстрактного класса до того, как значение помещается в <clinit> Object .

Я не уверен, что это на самом деле ошибка, но по крайней мере поведение непоследовательно. Я сообщил об этом несоответствии трекеру проблемы: https://youtrack.jetbrains.com/issue/KT-21764