Классы, объекты и конструкторы
Класс в Kotlin объявляется компактно: свойства и первичный конструктор часто помещаются в одну строку заголовка, а тело остаётся чистым.
Суть: понимание классов, конструкторов и свойств — основа объектно-ориентированного кода, на котором держатся и модели данных, и компоненты Android.
Класс — это шаблон для объектов. В Kotlin его объявляют словом class, а параметры первичного конструктора и свойства можно описать прямо в заголовке. Если перед параметром стоит val или var, он автоматически становится свойством объекта — не нужно отдельно объявлять поле и присваивать его.
class Person(val name: String, var age: Int)
val p = Person("Аня", 19) // создание без слова new
println(p.name) // Аня
p.age = 20 // age объявлен через var, можно менять
println(p.age) // 20Обратите внимание: в Kotlin нет ключевого слова new. Объект создаётся вызовом конструктора как обычной функции.
Блок init и вторичные конструкторы
Если при создании объекта нужно выполнить логику, её помещают в блок init. Он выполняется при создании объекта в порядке следования. Для альтернативных способов создания объекта есть вторичные конструкторы через constructor, но на практике чаще используют параметры по умолчанию в первичном.
class Rectangle(val width: Int, val height: Int) {
val area: Int
init {
require(width > 0 && height > 0) { "Размеры должны быть положительны" }
area = width * height // вычисляем при создании
}
}
val r = Rectangle(4, 5)
println(r.area) // 20Как работает под капотом
Когда вы пишете val name: String в конструкторе, компилятор создаёт приватное поле и публичный геттер. Для var добавляется ещё и сеттер. То есть привычные из Java геттеры и сеттеры генерируются автоматически, а обращение p.name на самом деле вызывает геттер. При желании можно задать своё поведение геттера или сеттера через кастомные аксессоры.
Создание объекта Person("Аня", 19)
1. Выделяется память под объект
2. Свойства name и age получают значения
3. Выполняются блоки init (по порядку)
4. Объект готов к использованию
p.name --> вызов сгенерированного геттераВычисляемые свойства
Свойство может не хранить значение, а вычислять его при каждом обращении через get(). Это удобно для производных данных.
class Circle(val radius: Double) {
val diameter: Double
get() = radius * 2 // вычисляется при каждом обращении
}
val c = Circle(5.0)
println(c.diameter) // 10.0Частые ошибки
Искать ключевое слово new. Его в Kotlin нет; объект создаётся вызовом конструктора.
Делать все свойства var. Если значение не должно меняться после создания, объявляйте его val — это безопаснее.
Перегружать класс логикой в init. Тяжёлые вычисления в init замедляют создание объекта; выносите их в методы или ленивые свойства.
Best practices
- Описывайте свойства прямо в первичном конструкторе через
val/var. - Предпочитайте
valдля неизменяемых свойств. - Используйте параметры по умолчанию вместо лишних вторичных конструкторов.
- Для производных данных применяйте вычисляемые свойства с
get().
Идею класса с конструктором и вычисляемой площадью легко повторить на Python. Запустите врезку.
# Аналог класса с конструктором и валидацией из Kotlin
class Rectangle:
def __init__(self, width, height):
assert width > 0 and height > 0, 'Размеры должны быть положительны'
self.width = width
self.height = height
self.area = width * height
r = Rectangle(4, 5)
print('площадь:', r.area)Попробуй сам ▶ — добавьте свойство периметра или метод масштабирования. В Kotlin поля задавались бы в заголовке конструктора, а вычисления — в блоке init.
Закрепим главное
Запомните, что компактность классов в Kotlin — не магия, а сгенерированный код. Свойство в конструкторе превращается в поле плюс аксессоры, а вы пишете одну строку вместо пятнадцати. Понимание этого помогает не бояться задавать кастомные геттеры и сеттеры, когда нужно особое поведение: вы просто берёте под контроль то, что обычно генерируется автоматически. Это даёт лаконичность по умолчанию и гибкость при необходимости.
Второй ориентир — иммутабельность как привычка проектирования. Спрашивайте себя про каждое свойство: должно ли оно меняться после создания объекта? Чаще ответ «нет» — и тогда val. Неизменяемые объекты проще читать, безопаснее передавать между потоками и предсказуемее в Compose, где состояние принято держать неизменяемым и обновлять через копию. Эта привычка окупится в каждом разделе курса, особенно когда мы дойдём до состояния экрана.
Итог: классы в Kotlin компактны благодаря свойствам в конструкторе и автоматическим аксессорам. Эти знания понадобятся сразу — в следующем уроке мы рассмотрим специальный вид классов для хранения данных.