Українська
Розбори та типові помилки
Розбір життєвого циклу: покрокове трасування
Наступна програма спеціально друкує кожен етап створення. Вона не моделює предметну область, а допомагає зіставити текст класу з порядком виконання. Функція mark повертає аргумент після повідомлення, тому може бути ініціалізатором. Перед запуском випишіть очікувану послідовність на папері.
kotlin
fun mark(text: String): String {
println(text)
return text
}
class Trace(name: String) {
val first = mark("property: $name")
init {
println("init 1")
}
val second = mark("second property")
init {
println("init 2")
}
constructor() : this("default") {
println("secondary body")
}
}
fun main() {
val item = Trace()
println("ready: ${item.first}")
}text
property: default
init 1
second property
init 2
secondary body
ready: property: defaultПорядок визначає розташування оголошень, а не їхню назву. Перенесення другого ініціалізатора вище першого init змінить послідовність. Не будуйте складних неявних залежностей між полями; краще мати невеликий зрозумілий конструктор. Якщо потрібний доступ до мережі або файлу, виконайте завантаження окремо й передайте готові перевірені дані моделі.
Під час винятку конструктор не повертає придатний екземпляр викликачу. Але зовнішні побічні ефекти, наприклад друк чи запис у файл, автоматично не скасовуються. Саме тому приклад із повідомленнями є навчальним трасуванням, а не стилем усіх конструкторів. Мінімум побічних ефектів полегшує перевірку.
Видимість аксесора на практиці
Модифікатор біля сетера змінює право запису, але не тип властивості й не право читання. Клієнт Account може побудувати звіт про баланс, а зміну виконує через предметний метод. Якщо саму властивість зробити private, читати її напряму зовні також не можна. Надавати публічний геттер слід лише для даних, які справді належать контракту користувача класу.
У Kotlin немає потреби вручну оголошувати Java-подібні методи getBalance і setBalance для кожного поля. Властивість дає короткий синтаксис клієнту й залишає можливість змінити реалізацію аксесорів. Не поєднуйте властивість balance з вручну написаним методом такої самої JVM-сигнатури: це може створити конфлікт під час компіляції.
Знімок екрана
Cursor inside Account; Alt+Insert, inspect available toString and constructor generation actions.
Рис. 5.7. Засоби генерування членів класу
Генератор IDE прискорює механічну частину, але не знає інваріантів. Автоматично створений конструктор перевірте на делегування та повторення правил. Згенерований toString перегляньте на розкриття зайвих даних. Натискання кнопки не замінює читання отриманого коду.
Знімок екрана
Show Kotlin Bytecode > Decompile Account; show backing balance field and public getBalance, restricted setter.
Рис. 5.8. Представлення властивостей для JVM
Порівняйте доступ до балансу в Kotlin і декомпільованому Java. Не переносіть деталі згенерованих імен у предметну модель: вони пояснюють сумісність платформ, а вихідний контракт залишається властивістю та методами. У наступній темі цей контракт стане основою поліморфного використання об’єктів.