Игнорировать setter и установить свойство напрямую

У меня есть класс, который записывает пользователя в SharedPreferences каждый раз, когда он установлен:

class UserManager @Inject constructor( val prefs: SharedPreferences, val jsonAdapter: JsonAdapter<User> ) { companion object { val USER = "user" } var user: User = User() set(value) { field = value prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply() } init { val userString = prefs.getString(USER, null) if (userString != null) { user = jsonAdapter.fromJson(userString) } } } 

Проблема. Если пользователь установлен в блоке init, он вызывает установщика и записывает пользователя, которого мы только что получили из общих префов … в общие префы.

Вопрос 1: Как я могу прямо установить свойство из блока init?

Вопрос 2: Почему я должен инициализировать пользователя, когда я определяю настраиваемый наборщик, но могу опустить инициализацию, когда используется установщик по умолчанию?

Вам нужно прямо инициализировать свойство с правильным значением. Вы можете сделать это, используя функцию run из stdlib:

 class UserManager @Inject constructor( val prefs: SharedPreferences, val jsonAdapter: JsonAdapter<User> ) { companion object { val USER = "user" } var user: User = run { val userString = prefs.getString(USER, null) if (userString != null) { jsonAdapter.fromJson(userString) } else { User() } } set(value) { field = value prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply() } } 

Более короткий синтаксис, предложенный Илья Рыженковым на Kotlin Slack:

 var user: User = prefs.getString(USER, null)?.let { jsonAdapter.fromJson(it) } ?: User() set(value) { field = value prefs.edit().putString(USER, jsonAdapter.toJson(user)).apply() } 

Я считаю, что лучшим решением является использование концепции «свойства поддержки», описанной здесь: https://kotlinlang.org/docs/reference/properties.html#backing-properties

 private var _table: Map<String, Int>? = null public val table: Map<String, Int> get() { if (_table == null) _table = HashMap() // Type parameters are inferred return _table ?: throw AssertionError("Set to null by another thread") } 

Затем инициализируйте свойство backing в конструкторе и сделайте <backingproperty> = value вместо <backingproperty> = value field = value а также укажите getter на свойство backing.

Взгляните на делегата by map , похоже, это шаблон, который вы хотите:

 class User(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map } 

https://kotlinlang.org/docs/reference/delegated-properties.html#storing-properties-in-a-map

Intereting Posts
Завод Котлин на внутреннем вложенном классе Как я могу использовать возврат вместо того, чтобы входить в Котлин? Android Kotlin создает реализацию класса Parcelable дает ошибку в «переопределении» метода writeToParcel Не удалось отобразить ошибку EditText в коде Kotlin Как передать аргументы компилятора компилятору Kotlin с Gradle Игнорирование определенных свойств при генерации equals (), hashCode () и т. Д. SocketException: сбой sendto: EBADF (дескриптор Bad файла) Использование констант kotlin в выражении java switch Неразрешенная ссылка: junit Тест модуля Kotlin не обнаруживает интерфейс зависимостей модуля В чем разница между обработкой полей через «accessor» и «copy» Чистая архитектура: ViewModel с несколькими UseCases на Android Spring Boot не запускает автозагрузчик при использовании @Cacheable Познакомиться с достопримечательностями в Котлин Библиотека Android, написанная в Kotlin, обеспечивает поддержку Java-приложений