TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Mobile VK Hub

25 Sep, 12:35

Открыть в Telegram Поделиться Пожаловаться

explicit backing fields: как убрать пару _state и state из каждой ViewModel

В любой ViewModel живёт одна и та же конструкция: приватный MutableStateFlow и публичный StateFlow поверх него. Две строки вместо одной, подчёркивание в имени, о котором когда-то договорились на ревью, и постоянный риск отдать наружу мутабельный тип по невнимательности.
class ProfileViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState.Loading)
val uiState: StateFlow = _uiState
}
В Kotlin 2.4 explicit backing fields стали стабильными, и эта пара схлопывается в одно свойство. Публичный тип объявляется как обычно, а ключевое слово field задаёт реальный тип хранения:
class ProfileViewModel : ViewModel() {
val uiState: StateFlow
field = MutableStateFlow(UiState.Loading)

fun retry() {
uiState.value = UiState.Loading // внутри класса это MutableStateFlow
}
}
Дальше всё решает точка обзора. Внутри класса компилятор видит поле и отдаёт MutableStateFlow, поэтому значение меняется привычным .value. Снаружи то же имя имеет тип StateFlow, и присвоить в него не выйдет. Гарантия осталась прежней, а лишнее имя ушло.

С коллекциями приём работает так же, и там он избавляет сразу от двух костылей: не нужен ни toList() на каждом обращении, ни отдельное неизменяемое представление.
class Cart {
val items: List
field = mutableListOf()

fun add(item: String) = items.add(item)
}
Требований к такому свойству немного, но соблюдаются они строго. Оно обязано быть val, без собственного геттера, не open и не делегированное. Тип поля должен быть подтипом типа свойства, а видимость у поля всегда приватная. Для var синтаксис не работает, так что изменяемые свойства остаются на старой схеме.

Снаружи не меняется ничего: collectAsStateWithLifecycle() в Compose и подписки в тестах видят прежний StateFlow. Под ту же схему попадают события через MutableSharedFlow и вообще любые пары, где внутри нужен изменяемый тип, а наружу отдаётся его неизменяемый родитель.

Сам синтаксис появился ещё в 2.3.0 в декабре 2025, но собирался только с флагом -Xexplicit-backing-fields. Теперь опт-ин не нужен, достаточно поднять версию Kotlin в проекте.
Переписывать разом весь модуль смысла мало.

Больше всего выигрывают классы, где таких пар несколько: экранные ViewModel с отдельными потоками для контента, ошибок и прогресса. Там уходит половина объявлений, а вместе с ними и путаница, к какому из двух имён обращаться.

#mobilevkhub #kotlin

266 0 2 8
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot