Что такое делегированные характеристики (Delegated properties)?android-229

Основная концепция

Делегированные свойства — это свойства, методы доступа к которым (геттеры/сеттеры) делегируются специальному объекту. Это позволяет вынести общую логику работы со свойствами в отдельные компоненты.

Синтаксис и базовый пример

class Example {
    var delegatedProperty: String by Delegate()
}

Здесь управление доступом к delegatedProperty передается объекту класса Delegate.

Стандартные делегаты в Kotlin

1. lazy - отложенная инициализация

val heavyObject: HeavyResource by lazy {
    HeavyResource() // Создается только при первом обращении
}

2. observable - отслеживание изменений

var observableProperty: String by Delegates.observable("initial") {
    prop, old, new ->
    println("$old → $new")
}

3. vetoable - валидация значений

var positiveNumber: Int by Delegates.vetoable(0) {
    _, old, new -> new >= 0
}

4. map - хранение в Map

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

Создание собственного делегата

Для создания кастомного делегата нужно реализовать интерфейс ReadOnlyProperty (для val) или ReadWriteProperty (для var):

class CustomDelegate : ReadWriteProperty<Any?, String> {
    private var storedValue = "default"

    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("Getting value: $storedValue")
        return storedValue
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Setting value: $value")
        storedValue = value
    }
}

Практические примеры использования

1. Логирование доступа к свойствам

class LoggingDelegate<T> : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        println("Accessed ${property.name}: $value")
        return value ?: throw IllegalStateException()
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        println("Updated ${property.name}: $value")
        this.value = value
    }
}

2. SharedPreferences делегат

class PrefsDelegate<T>(
    private val prefs: SharedPreferences,
    private val key: String,
    private val defaultValue: T
) : ReadWriteProperty<Any?, T> {

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return when (defaultValue) {
            is String -> prefs.getString(key, defaultValue)
            is Int -> prefs.getInt(key, defaultValue)
            // другие типы
            else -> throw IllegalArgumentException()
        } as T
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        prefs.edit().apply {
            when (value) {
                is String -> putString(key, value)
                is Int -> putInt(key, value)
                // другие типы
                else -> throw IllegalArgumentException()
            }
        }.apply()
    }
}

Преимущества делегированных свойств

  1. Уменьшение дублирования кода - общая логика выносится в делегаты
  2. Читаемость - сложная логика инкапсулирована
  3. Гибкость - можно легко менять поведение свойств
  4. Стандартные решения - для распространенных задач

Ограничения и подводные камни

  1. Отладка - может усложнить трассировку кода
  2. Производительность - небольшой оверхед по сравнению с обычными свойствами
  3. Прозрачность - поведение неочевидно при беглом просмотре кода

Резюмируем:

Делегированные свойства в Kotlin — это мощный механизм для выноса логики работы с полями класса в отдельные компоненты. Они позволяют создавать элегантные, повторно используемые решения для управления доступом к данным, валидации, наблюдения за изменениями и других распространенных задач.