fbpx

Blog

Делегированные свойства в Kotlin

Существует несколько основных видов свойств, которые мы реализовываем каждый раз вручную в случае их надобности. Однако намного удобнее было бы реализовать их раз и навсегда и положить в какую-нибудь библиотеку. Сегодня рассмотрим 4 интересных так называемых delegate properties, которые могут быть полезны при разработке Android-приложений на языке Kotlin.

Итак мы разберем следующие делегированные свойства:

  • Lazy
  • Observable
  • Vetoable
  • notNull

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

Lazy

lazy() это функция, которая принимает лямбду и возвращает экземпляр класса Lazy<T>, который служит делегатом для реализации ленивого свойства: первый вызов get() запускает лямбда-выражение, переданное lazy() в качестве аргумента, и запоминает полученное значение, а последующие вызовы просто возвращают вычисленное значение.

val str by lazy { "I'm lazily initialized" }

Тут может возникнуть вопрос (и такие вопросы часто возникают на собеседовании), а именно, синхронизированы ли вычисления при обработке кода несколькими потоками? По умолчанию вычисление ленивых свойств синхронизировано: значение вычисляется только в одном потоке выполнения, и все остальные потоки могут видеть одно и то же значение. Однако, если вы уверены, что данный код будет выполнять только в одном потоке, то вы можете управлять способом вычисления lazy следующими параметрами, передав один из них в качестве аргумента функции lazy()

enum class LazyThreadSafetyMode{
      SYNCHRONIZED,
      PUBLICATION,
      NONE
  }
  • Если вы не передадите никакое значение в lazy() то будет использоваться потокобезопасный режим SYNCHRONIZED.
  • Режим PUBLICATION означает, что блок в lazy() будет доступен нескольким потокам, однако значение будет возвращено первое, которое подсчитается.
  • Режим NONE не является потокобезопасным и должен использоваться крайне редко – когда вы действительно уверены, что данный блок кода будет исполняться в одном потоке.

Observable

Данное свойство позволяет вызывать некую функцию каждый раз, когда значение изменилось. Функция Delegates.observable() принимает два аргумента: начальное значение свойства и обработчик (лямбда), который вызывается при изменении свойства. У обработчика три параметра: описание свойства, которое изменяется, старое значение и новое значение.

var observable by Delegates.observable(1) { prop, oldVal, newVal ->
 println("Observable property changed from $oldVal to $newVal")
 }

Теперь, если мы изменим значение, то будет вызвана лямбда

// Изменение значение напечатает Observable property changed from 1 // to 0
 observable = 0

Vetoable

Это свойство похоже на Observable. С помощью vetoable можно узнать, когда значение меняется и тем самым, например добавить проверку перед присваиванием значения. Например:

var vetoable by Delegates.vetoable(1) { prop, oldVal, newVal ->
    return@vetoable newVal <= 10
}

Если мы попробуем поменять значение, то изменение будет заблокировано, так как нам подходят значения <= 10

//change is blocked
vetoable = 100

Если лямбда вернет true – то все ок, значение можно менять

//changed to 0
vetoable = 0

notNull

Это свойство похоже на lateinit. Оно позволяет объявить свойство без начального значения. Если попытаться прочитать значение до того, как присвоили значение – будет брошено исключение.

var notNull by Delegates.notNull()

Итак, мы рассмотрели 4 delegate properties Lazy, Observable, Vetoable и notNull. Надеюсь данный материал был полезен и полученные знания сделают ваш код более изящным и читабельным. Также рекомендую взглянуть на документацию.