Классы данных и запечатанные классы
Используйте классы данных для сравнения по значениям и функцию copy(), а запечатанные классы — для исчерпывающих иерархий типов и ясного моделирования состояний.
«Классы данных и запечатанные классы» — бесплатный урок Android Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Что такое классы данных?
Класс данных в Kotlin — это класс, основное назначение которого состоит в хранении данных. Компилятор автоматически создаёт для Вас полезные методы:
equals()— сравнивает два экземпляра по значениюhashCode()— согласованный хеш на основе свойствtoString()— понятное строковое представлениеcopy()— создаёт изменённую копию- Поддержка деструктуризации
Объявление класса данных
Добавьте ключевое слово data перед class. Все свойства должны находиться в основном конструкторе:
data class User(val id: Int, val name: String, val email: String)
fun main() {
val u1 = User(1, "Alice", "alice@example.com")
val u2 = User(1, "Alice", "alice@example.com")
println(u1) // User(id=1, name=Alice, email=alice@example.com)
println(u1 == u2) // true (value equality)
println(u1 === u2) // false (different objects)
}copy() — изменение одного поля
copy() создаёт новый экземпляр с изменёнными свойствами. Исходный экземпляр не изменяется (неизменяемость):
data class Product(val name: String, val price: Double, val inStock: Boolean)
fun main() {
val original = Product("Laptop", 999.0, true)
// Only change the price; keep everything else
val onSale = original.copy(price = 799.0)
println(original) // Product(name=Laptop, price=999.0, inStock=true)
println(onSale) // Product(name=Laptop, price=799.0, inStock=true)
}Деструктурирующие объявления
Классы данных поддерживают деструктуризацию — распаковку свойств в переменные с помощью функций componentN(), сгенерированных компилятором:
data class Point(val x: Int, val y: Int)
fun main() {
val point = Point(10, 20)
// Destructure into separate variables
val (x, y) = point
println("x=$x, y=$y") // x=10, y=20
// Also works in for loops with maps
val scores = mapOf("Alice" to 95, "Bob" to 88)
for ((name, score) in scores) {
println("$name: $score")
}
}Что такое запечатанные классы?
Запечатанный класс — это ограниченная иерархия классов: все подклассы должны быть определены в одном файле. Благодаря этому набор возможных типов известен во время компиляции.
Используйте запечатанные классы для представления конечного набора состояний, например результатов сети, состояний интерфейса или событий навигации.
Синтаксис запечатанного класса
Объявляйте подклассы непосредственно внутри класса или в том же файле:
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val message: String, val code: Int = 0) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}
fun handle(result: NetworkResult<String>) {
when (result) {
is NetworkResult.Success -> println("Data: ${result.data}")
is NetworkResult.Error -> println("Error ${result.code}: ${result.message}")
NetworkResult.Loading -> println("Loading...")
}
}Запечатанный класс + when (полный перебор)
Главное преимущество запечатанных классов: выражение when является полным. Компилятор заставляет обработать каждый подтип — ни один случай не будет забыт:
sealed class UiState {
object Loading : UiState()
data class Success(val message: String) : UiState()
data class Error(val error: String) : UiState()
}
fun render(state: UiState): String = when (state) {
is UiState.Loading -> "Showing spinner"
is UiState.Success -> "Show: ${state.message}"
is UiState.Error -> "Error: ${state.error}"
// Compiler error if you remove any branch!
}
fun main() {
println(render(UiState.Loading))
println(render(UiState.Success("Loaded!")))
}Запечатанный класс и перечисление
Оба варианта представляют фиксированный набор значений, но:
- Перечисление — каждая константа является отдельным экземпляром, без дополнительных данных для каждого варианта
- Запечатанный класс — каждый вариант может содержать разные данные и быть классом или объектом
Используйте перечисление, если вариантам не нужны данные. Используйте запечатанный класс, если вариантам нужны разные свойства.
Объявления объектов (одиночные экземпляры)
object создаёт одиночный экземпляр — один экземпляр, создаваемый при первом обращении, с безопасностью для многопоточности:
object AppConfig {
const val BASE_URL = "https://api.example.com"
const val TIMEOUT = 30L
fun buildUrl(path: String) = "$BASE_URL/$path"
}
fun main() {
println(AppConfig.BASE_URL) // https://api.example.com
println(AppConfig.buildUrl("users")) // https://api.example.com/users
}Сопутствующие объекты
Сопутствующий объект находится внутри класса и предоставляет элементы уровня класса (как static в Java). Обычно он используется для фабричных методов:
class Token private constructor(val value: String) {
companion object {
fun create(raw: String): Token {
val cleaned = raw.trim()
require(cleaned.isNotEmpty()) { "Token must not be blank" }
return Token(cleaned)
}
}
}
fun main() {
val token = Token.create(" abc123 ")
println(token.value) // abc123
}Классы данных в настоящем приложении
Объединяйте классы данных и запечатанные классы для удобного моделирования состояний:
data class Post(val id: Int, val title: String, val body: String)
sealed class PostState {
object Loading : PostState()
data class Loaded(val posts: List<Post>) : PostState()
data class Failed(val reason: String) : PostState()
}
fun showState(state: PostState) = when (state) {
PostState.Loading -> println("Fetching posts...")
is PostState.Loaded -> println("${state.posts.size} posts loaded")
is PostState.Failed -> println("Failed: ${state.reason}")
}
fun main() {
showState(PostState.Loaded(listOf(
Post(1, "Hello", "World"),
Post(2, "Kotlin", "Rocks")
)))
}Быстрая проверка
Какое утверждение лучше всего описывает отличие запечатанных классов от обычных абстрактных классов?
Итоги: классы данных и запечатанные классы
Теперь Вы знаете два мощных инструмента Kotlin:
- класс данных — сравнение по значениям,
copy(), деструктуризация, автоматически сгенерированныйtoString() - запечатанный класс — конечная иерархия типов, полный
when, каждый вариант может содержать собственные данные - объект — одиночный экземпляр, сопутствующие объекты для фабричных методов
Вместе эти подходы заменяют шаблонный код Java и позволяют чисто и безопасно моделировать состояния.
Часто задаваемые вопросы
Урок «Классы данных и запечатанные классы» бесплатный?
Да — полный текст урока «Классы данных и запечатанные классы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Классы данных и запечатанные классы»?
Используйте классы данных для сравнения по значениям и функцию copy(), а запечатанные классы — для исчерпывающих иерархий типов и ясного моделирования состояний. Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Классы данных и запечатанные классы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Безопасность работы с null
- Классы и объекты
- Классы данных и запечатанные классы
- Функции-расширения и функции высшего порядка