Під час розробки для android я іноді стикаюся з таким виглядом:
var someModel: someViewModel by notNullAndObservable { vm ->
...
}
Я не розумію, яке значення має by
ключове слово.
Відповіді:
У посиланні на Kotlin ви знайдете два способи використання by
, причому перше - делеговані властивості, тобто використання, яке ви використовували вище:
Існують певні загальні типи властивостей, які, хоча ми можемо реалізовувати їх вручну кожного разу, коли вони нам потрібні, було б дуже приємно впровадити раз і назавжди та помістити їх у бібліотеку. Приклади включають ледачі властивості: значення обчислюється лише при першому доступі, спостережувані властивості: слухачі отримують сповіщення про зміни в цій властивості, зберігаючи властивості на карті, а не в окремому полі.
Тут ви делегуєте getter / setter до іншого класу, який виконує роботу і може містити загальний код. В якості іншого прикладу, деякі інжектори залежностей для Kotlin підтримують цю модель, делегуючи геттер для отримання значення з реєстру екземплярів, керованих механізмом введення залежностей.
І передача інтерфейсу / класу - це інше використання:
Шаблон делегування виявився хорошою альтернативою успадкуванню реалізації, і Kotlin підтримує його, що вимагає від нуля типового коду. Клас Похідний може успадкувати від бази інтерфейсу та делегувати всі його загальнодоступні методи вказаному об'єкту
Тут ви можете делегувати інтерфейс іншій реалізації, тому виконуючому класу потрібно лише перевизначити те, що він хоче змінити, тоді як решта методів делегують назад до повнішої реалізації.
Живим прикладом може бути колекція Klutter Readonly / Immutable, де вони насправді просто делегують конкретний інтерфейс колекції іншому класу, а потім замінюють все, що має бути іншим у реалізації лише для читання. Заощаджуючи багато роботи, не потрібно вручну делегувати всі інші методи.
Обидва вони охоплені посиланням на мову Kotlin , починаючи з базових тем мови.
Простими словами, ви можете зрозуміти by
ключове слово, як це передбачено .
З точки зору споживача власності, val
це те, що має getter (отримати), і var
це те, що має getter і setter (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 , іншими словами, якщо ми викликаємо метод get () властивості p , викликається метод getValue () екземпляра делегата .
Якщо ми намагаємося встановити значення властивості p , іншими словами, якщо ми викликаємо метод set () властивості p , буде викликаний метод setValue () екземпляра делегата .
Делегування власності:
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