При разработке под android я иногда сталкиваюсь с чем-то вроде этого:
var someModel: someViewModel by notNullAndObservable { vm ->
...
}
Я не понимаю значения этого by
ключевого слова.
Ответы:
В справочнике Kotlin вы найдете два использования by
, первое из которых - это делегированные свойства, которые вы использовали выше:
Существуют определенные общие типы свойств, которые, хотя мы можем реализовать их вручную каждый раз, когда они нам нужны, было бы очень хорошо реализовать раз и навсегда и поместить в библиотеку. Примеры включают ленивые свойства: значение вычисляется только при первом доступе, наблюдаемые свойства: слушатели получают уведомления об изменениях этого свойства, сохраняя свойства на карте, а не в каждом отдельном поле.
Здесь вы делегируете геттер / сеттер другому классу, который выполняет эту работу и может содержать общий код. В качестве другого примера, некоторые из инжекторов зависимостей для Kotlin поддерживают эту модель, делегируя геттеру получение значения из реестра экземпляров, управляемых механизмом внедрения зависимостей.
И делегирование интерфейса / класса - другое использование:
Шаблон делегирования оказался хорошей альтернативой наследованию реализации, и Kotlin поддерживает его, изначально не требуя шаблонного кода. Класс Derived может наследовать от интерфейса Base и делегировать все свои общедоступные методы указанному объекту.
Здесь вы можете делегировать интерфейс другой реализации, поэтому классу реализации нужно только переопределить то, что он хочет изменить, в то время как остальные методы делегируются обратно более полной реализации.
Живым примером могут быть коллекции Klutter Readonly / Immutable, где они действительно просто делегируют конкретный интерфейс коллекции другому классу, а затем переопределяют все, что должно отличаться в реализации только для чтения. Экономия большого объема работы без необходимости вручную делегировать все другие методы.
Обе они описаны в справочнике по языку Kotlin , начиная с него, чтобы познакомиться с базовыми темами языка.
Проще говоря, вы можете понять by
ключевое слово как предоставленное .
С точки зрения потребителя свойства, val
это то, что имеет геттер (get) и var
то, что имеет геттер и сеттер (get, set). Для каждого var
свойства есть поставщик по умолчанию для методов get и set, который нам не нужно указывать явно.
Но, используя by
ключевое слово, вы утверждаете, что этот метод получения / получения и установки предоставляется где-то еще (т.е. он был делегирован). Это обеспечивается с помощью функции , которая приходит после by
.
Таким образом, вместо использования этих встроенных методов get и set вы делегируете эту работу какой-то явной функции.
Один очень распространенный пример - by lazy
свойства отложенной загрузки. Кроме того, если вы используете библиотеку внедрения зависимостей, такую как Koin, вы увидите множество свойств, определенных следующим образом:
var myRepository: MyRepository by inject() //inject is a function from Koin
В определении класса он следует тому же принципу, он определяет, где предоставляется какая-либо функция, но может относиться к любому набору методов / свойств, а не только к получению и установке.
class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface
Этот код говорит: «Я класс MyClass, и я предлагаю функции интерфейса SomeInterface, которые предоставляются SomeImplementation. Я сам буду реализовывать SomeOtherInterface (это неявно, так что нет by
) ».
Синтаксис:
val/var <property name>: <Type> by <expression>.
Выражение после by является делегатом
если попытаться получить доступ к значению свойств р , иными словами, если мы называем получить () метод свойств р , то ПолучитьЗначение () метод делегата экземпляра .
Если мы попытаемся установить значение свойства p , другими словами, если мы вызовем метод set () свойства p , будет вызван метод setValue () экземпляра Delegate .
Делегирование на имущество:
import kotlin.reflect.KProperty
class Delegate {
// for get() method, ref - a reference to the object from
// which property is read. prop - property
operator fun getValue(ref: Any?, prop: KProperty<*>) = "textA"
// for set() method, 'v' stores the assigned value
operator fun setValue(ref: Any?, prop: KProperty<*>, v: String) {
println("value = $v")
}
}
object SampleBy {
var s: String by Delegate() // delegation for property
@JvmStatic fun main(args: Array<String>) {
println(s)
s = "textB"
}
}
Результат:
textA
value = textB
Делегация на занятие:
interface BaseInterface {
val value: String
fun f()
}
class ClassA: BaseInterface {
override val value = "property from ClassA"
override fun f() { println("fun from ClassA") }
}
// The ClassB can implement the BaseInterface by delegating all public
// members from the ClassA.
class ClassB(classA: BaseInterface): BaseInterface by classA {}
object SampleBy {
@JvmStatic fun main(args: Array<String>) {
val classB = ClassB(ClassA())
println(classB.value)
classB.f()
}
}
Результат:
property from ClassA
fun from ClassA
Делегирование параметров:
// for val properties Map is used; for var MutableMap is used
class User(mapA: Map<String, Any?>, mapB: MutableMap<String, Any?>) {
val name: String by mapA
val age: Int by mapA
var address: String by mapB
var id: Long by mapB
}
object SampleBy {
@JvmStatic fun main(args: Array<String>) {
val user = User(mapOf("name" to "John", "age" to 30),
mutableMapOf("address" to "city, street", "id" to 5000L))
println("name: ${user.name}; age: ${user.age}; " +
"address: ${user.address}; id: ${user.id}")
}
}
Результат:
name: John; age: 30; address: city, street; id: 5000