← Назад к вопросам
Приведи пример неизменного кода
2.0 Middle🔥 83 комментариев
#Многопоточность и асинхронность
Комментарии (3)
🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример неизменяемого класса в Kotlin
Ключевые принципы иммутабельности
Неизменяемый (immutable) код — это код, состояние объектов которого нельзя изменить после создания. Основные характеристики:
- Все поля объявлены как
val(только для чтения) - Класс помечен как
data class(для автоматической реализацииequals(),hashCode(),toString()) - Поля являются либо примитивами, либо ссылками на другие неизменяемые объекты
- Нет сеттеров (setters)
- Класс может быть объявлен как
sealed classдля ограниченной иерархии
Полный пример неизменяемого класса
/**
* Неизменяемый класс, представляющий почтовый адрес.
* Все поля - val, класс - data class, сеттеры отсутствуют.
*/
data class ImmutableAddress(
val street: String,
val houseNumber: Int,
val apartment: Int? = null,
val city: String,
val postalCode: String
) {
/**
* Вторичный конструктор с валидацией.
* Не изменяет состояние, только проверяет входные данные.
*/
constructor(
street: String,
houseNumber: Int,
city: String,
postalCode: String
) : this(street, houseNumber, null, city, postalCode) {
require(houseNumber > 0) { "Номер дома должен быть положительным" }
require(postalCode.matches(Regex("\\d{6}"))) { "Неверный формат почтового индекса" }
}
/**
* Методы, возвращающие новые объекты вместо изменения текущего.
* Это называется "functional update" или "copy-and-update".
*/
fun withApartment(newApartment: Int): ImmutableAddress {
require(newApartment > 0) { "Номер квартиры должен быть положительным" }
return this.copy(apartment = newApartment)
}
fun formattedAddress(): String {
return buildString {
append("ул. $street, д. $houseNumber")
apartment?.let { append(", кв. $it") }
append(", $city, $postalCode")
}
}
}
/**
* Неизменяемый класс, использующий другой неизменяемый класс как поле.
*/
data class ImmutableUser(
val id: UUID,
val name: String,
val email: String,
val address: ImmutableAddress,
val registrationDate: LocalDate = LocalDate.now()
) {
init {
require(name.isNotBlank()) { "Имя не может быть пустым" }
require(email.contains("@")) { "Некорректный email" }
}
/**
* Метод, возвращающий новый объект с обновленным адресом.
* Исходный объект остается неизменным.
*/
fun updateAddress(newAddress: ImmutableAddress): ImmutableUser {
return this.copy(address = newAddress)
}
/**
* Метод, возвращающий нового пользователя с измененным именем.
*/
fun rename(newName: String): ImmutableUser {
require(newName.isNotBlank()) { "Имя не может быть пустым" }
return this.copy(name = newName)
}
}
/**
* Sealed class как пример неизменяемой иерархии классов.
*/
sealed class ImmutablePaymentMethod {
data class CreditCard(
val cardNumber: String,
val expiryDate: YearMonth,
val cardholderName: String
) : ImmutablePaymentMethod()
data class BankTransfer(
val accountNumber: String,
val bankName: String
) : ImmutablePaymentMethod()
object Cash : ImmutablePaymentMethod()
}
Пример использования
fun main() {
// Создание неизменяемого объекта
val address = ImmutableAddress(
street = "Ленина",
houseNumber = 42,
city = "Москва",
postalCode = "123456"
)
println("Исходный адрес: ${address.formattedAddress()}")
// Исходный адрес: ул. Ленина, д. 42, Москва, 123456
// Создание нового объекта на основе существующего
val addressWithApartment = address.withApartment(15)
println("Адрес с квартирой: ${addressWithApartment.formattedAddress()}")
// Адрес с квартирой: ул. Ленина, д. 42, кв. 15, Москва, 123456
// Исходный объект не изменился
println("Исходный адрес после withApartment: ${address.formattedAddress()}")
// Исходный адрес после withApartment: ул. Ленина, д. 42, Москва, 123456
// Использование copy() для создания модифицированной копии
val modifiedAddress = address.copy(street = "Пушкина", houseNumber = 10)
println("Измененный адрес: ${modifiedAddress.formattedAddress()}")
// Измененный адрес: ул. Пушкина, д. 10, Москва, 123456
// Создание пользователя с адресом
val user = ImmutableUser(
id = UUID.randomUUID(),
name = "Иван Иванов",
email = "ivan@example.com",
address = address
)
// Обновление адреса пользователя
val userWithNewAddress = user.updateAddress(addressWithApartment)
println("Пользователь с новым адресом: ${userWithNewAddress.address.formattedAddress()}")
// Пользователь с новым адресом: ул. Ленина, д. 42, кв. 15, Москва, 123456
}
Преимущества неизменяемого кода
- Потокобезопасность — неизменяемые объекты можно безопасно использовать в многопоточных средах без синхронизации
- Предсказуемость — состояние объекта не может неожиданно измениться
- Кэшируемость — объекты можно безопасно кэшировать, так как их состояние постоянно
- Простота тестирования — не нужно учитывать изменение состояния объекта
- Безопасность при передаче — можно безопасно передавать объекты между компонентами системы
Особенности в Android контексте
В Android разработке неизменяемые объекты особенно полезны для:
- Моделей данных в MVVM/MVI архитектурах
- Состояния UI (State в Jetpack Compose)
- Конфигурационных объектов
- Событий (Events) в event-driven архитектурах
// Пример неизменяемого состояния UI в Compose
data class ProfileScreenState(
val user: ImmutableUser? = null,
val isLoading: Boolean = false,
val errorMessage: String? = null
) {
val isError: Boolean get() = errorMessage != null
}
Неизменяемый код способствует написанию более чистого, поддерживаемого и надежного приложения, минимизируя побочные эффекты и упрощая отладку.