← Назад к вопросам

Приведи пример неизменного кода

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
}

Преимущества неизменяемого кода

  1. Потокобезопасность — неизменяемые объекты можно безопасно использовать в многопоточных средах без синхронизации
  2. Предсказуемость — состояние объекта не может неожиданно измениться
  3. Кэшируемость — объекты можно безопасно кэшировать, так как их состояние постоянно
  4. Простота тестирования — не нужно учитывать изменение состояния объекта
  5. Безопасность при передаче — можно безопасно передавать объекты между компонентами системы

Особенности в 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
}

Неизменяемый код способствует написанию более чистого, поддерживаемого и надежного приложения, минимизируя побочные эффекты и упрощая отладку.

Приведи пример неизменного кода | PrepBro