Kotlin Academy · Урок

Делегаты для настроек, свойств на основе карт и ведения журнала

Применяйте делегаты к SharedPreferences, хранилищу на основе карт и аудиту.

Урок 4 из 413 шагов

«Делегаты для настроек, свойств на основе карт и ведения журнала» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Свойства на основе карты

Kotlin позволяет напрямую делегировать свойства объекту Map. При чтении свойства его имя используется для поиска значения в карте.

class User(map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}
fun main() {
    val u = User(mapOf("name" to "Alice", "age" to 30))
    println(u.name) // Alice
    println(u.age)  // 30
}

Делегат MutableMap

Для изменяемых свойств делегируйте их объекту MutableMap, и изменения будут обновлять базовую карту.

class Settings(private val map: MutableMap<String, Any?> = mutableMapOf()) {
    var theme: String by map
    var fontSize: Int by map
}
fun main() {
    val s = Settings()
    s.theme = "dark"
    s.fontSize = 16
    println(s.theme)    // dark
    println(s.fontSize) // 16
}

Десериализация JSON с делегатом карты

Разберите карту, похожую на JSON, и предоставьте доступ к её полям как к типизированным свойствам с помощью делегирования карте — это особенно удобно для динамических данных.

class ApiResponse(data: Map<String, Any?>) {
    val id: Int by data
    val title: String by data
    val published: Boolean by data
}
fun main() {
    val response = ApiResponse(mapOf("id" to 1, "title" to "Kotlin Rocks", "published" to true))
    println(response.title)     // Kotlin Rocks
    println(response.published) // true
}

Шаблон делегата с журналированием

Оберните любой делегат журналированием, чтобы прозрачно отслеживать каждое чтение и запись во всех делегированных свойствах.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class Logged<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(t: Any?, p: KProperty<*>): T {
        println("[READ]  ${p.name} = $v"); return v
    }
    override fun setValue(t: Any?, p: KProperty<*>, value: T) {
        println("[WRITE] ${p.name}: $v -> $value"); v = value
    }
}
var config: String by Logged("default")
fun main() { println(config); config = "production" }

Сочетание карты и журналирования

Добавьте оболочку с журналированием поверх делегирования карте, чтобы полностью отслеживать доступ к динамическим свойствам.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class TrackedMap(private val map: MutableMap<String,Any?> = mutableMapOf()) {
    inner class Delegate<T> : ReadWriteProperty<Any?,T> {
        @Suppress("UNCHECKED_CAST")
        override fun getValue(t:Any?,p:KProperty<*>): T {
            val v = map[p.name] as T; println("read ${p.name}=$v"); return v
        }
        override fun setValue(t:Any?,p:KProperty<*>,value:T) {
            println("write ${p.name}=$value"); map[p.name]=value
        }
    }
    var host: String by Delegate()
}
fun main() { val m=TrackedMap(); m.host="localhost"; println(m.host) }

Делегат в стиле SharedPreferences

Делегат хранит значения в базовой карте, имитируя SharedPreferences. Чтобы адаптировать его к настоящим настройкам Android, замените карту вызовами реальных настроек.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val prefs = mutableMapOf<String, Any?>()
fun <T> pref(default: T): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t: Any?, p: KProperty<*>): T = prefs.getOrDefault(p.name, default) as T
    override fun setValue(t: Any?, p: KProperty<*>, value: T) { prefs[p.name] = value }
}
object UserPrefs {
    var username: String by pref("Guest")
    var isDarkMode: Boolean by pref(false)
}
fun main() {
    println(UserPrefs.username)   // Guest
    UserPrefs.username = "Alice"
    println(UserPrefs.username)   // Alice
}

Типизированные помощники для настроек

Создайте типизированные вспомогательные функции для каждого примитивного типа настройки, чтобы упростить места вызова.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val store = mutableMapOf<String,Any?>()
fun stringPref(default: String) = object : ReadWriteProperty<Any?,String> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>) = store.getOrDefault(p.name, default) as String
    override fun setValue(t:Any?,p:KProperty<*>,v:String) { store[p.name]=v }
}
fun intPref(default: Int) = object : ReadWriteProperty<Any?,Int> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>) = store.getOrDefault(p.name, default) as Int
    override fun setValue(t:Any?,p:KProperty<*>,v:Int) { store[p.name]=v }
}
object Prefs {
    var name: String by stringPref("default")
    var count: Int by intPref(0)
}
fun main() { Prefs.name="Bob"; Prefs.count=5; println("${Prefs.name}: ${Prefs.count}") }

Делегат журнала аудита

Записывайте каждое изменение свойства вместе с отметкой времени в список аудита — это удобно для сценариев, связанных с соответствием требованиям или отладкой.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val audit = mutableListOf<String>()
class AuditDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(t:Any?,p:KProperty<*>) = v
    override fun setValue(t:Any?,p:KProperty<*>,value:T) {
        audit += "[${System.currentTimeMillis()}] ${p.name}: $v -> $value"
        v = value
    }
}
var price: Double by AuditDelegate(9.99)
fun main() {
    price = 12.99
    price = 11.49
    audit.forEach(::println)
}

Проверка и хранение в карте

Сочетайте хранение в карте с проверкой: отклоняйте недопустимые значения до их записи в карту.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val data = mutableMapOf<String, Any?>()
fun positiveInt(default: Int): ReadWriteProperty<Any?,Int> = object : ReadWriteProperty<Any?,Int> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>)=data.getOrDefault(p.name,default) as Int
    override fun setValue(t:Any?,p:KProperty<*>,v:Int){
        require(v>=0){"${p.name} must be >= 0"}
        data[p.name]=v
    }
}
object Inventory { var stock: Int by positiveInt(100) }
fun main(){
    Inventory.stock=50; println(Inventory.stock)
    try { Inventory.stock=-1 } catch(e:Exception){ println(e.message) }
}

Шаблон композиции делегатов

Объединяйте несколько вариантов поведения делегата с помощью API в стиле построителя: начните с базового делегата и добавьте журналирование, проверку или кэширование.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T> ReadWriteProperty<Any?,T>.withLogging(): ReadWriteProperty<Any?,T> {
    val base = this
    return object : ReadWriteProperty<Any?,T> {
        override fun getValue(t:Any?,p:KProperty<*>): T {
            val v = base.getValue(t,p); println("get ${p.name}=$v"); return v
        }
        override fun setValue(t:Any?,p:KProperty<*>,v:T){
            println("set ${p.name}=$v"); base.setValue(t,p,v)
        }
    }
}

Практический пример: шаблон объекта конфигурации

Используйте делегирование карте, чтобы создать объект конфигурации из внешних источников, например переменных окружения или файла конфигурации JSON.

class AppConfig(private val raw: Map<String, Any?>) {
    val host: String by raw
    val port: Int by raw
    val debug: Boolean by raw
}
fun main() {
    val env = mapOf("host" to "0.0.0.0", "port" to 8080, "debug" to true)
    val cfg = AppConfig(env)
    println("${cfg.host}:${cfg.port} debug=${cfg.debug}")
}

Быстрая проверка

Что происходит, когда свойство делегируется объекту Map?

Итоги

Делегаты открывают мощные возможности: делегирование карте для динамических данных и данных JSON, сохранение в стиле SharedPreferences, журналирование аудита для соответствия требованиям и комбинируемую проверку — без изменения мест вызова.

Можно начать бесплатно

Изучай Kotlin с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
51
Уроки
203

Часто задаваемые вопросы

Урок «Делегаты для настроек, свойств на основе карт и ведения журнала» бесплатный?

Да — полный текст урока «Делегаты для настроек, свойств на основе карт и ведения журнала» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Делегаты для настроек, свойств на основе карт и ведения журнала»?

Применяйте делегаты к SharedPreferences, хранилищу на основе карт и аудиту. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Делегаты для настроек, свойств на основе карт и ведения журнала»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. by lazy: подробный разбор отложенной инициализации
  2. Делегаты observable и vetoable
  3. Написание пользовательского делегата ReadWriteProperty
  4. Делегаты для настроек, свойств на основе карт и ведения журнала
← Назад к Kotlin Academy