Что делает ключевое слово by в Котлине?

Ответы:

76

В справочнике Kotlin вы найдете два использования by, первое из которых - это делегированные свойства, которые вы использовали выше:

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

Здесь вы делегируете геттер / сеттер другому классу, который выполняет эту работу и может содержать общий код. В качестве другого примера, некоторые из инжекторов зависимостей для Kotlin поддерживают эту модель, делегируя геттеру получение значения из реестра экземпляров, управляемых механизмом внедрения зависимостей.

И делегирование интерфейса / класса - другое использование:

Шаблон делегирования оказался хорошей альтернативой наследованию реализации, и Kotlin поддерживает его, изначально не требуя шаблонного кода. Класс Derived может наследовать от интерфейса Base и делегировать все свои общедоступные методы указанному объекту.

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

Живым примером могут быть коллекции Klutter Readonly / Immutable, где они действительно просто делегируют конкретный интерфейс коллекции другому классу, а затем переопределяют все, что должно отличаться в реализации только для чтения. Экономия большого объема работы без необходимости вручную делегировать все другие методы.

Обе они описаны в справочнике по языку Kotlin , начиная с него, чтобы познакомиться с базовыми темами языка.

Джейсон Минард
источник
93

Проще говоря, вы можете понять 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) ».

Daneejela
источник
23

введите описание изображения здесь

Синтаксис:

val/var <property name>: <Type> by <expression>. 

Выражение после by является делегатом

если попытаться получить доступ к значению свойств р , иными словами, если мы называем получить () метод свойств р , то ПолучитьЗначение () метод делегата экземпляра .

Если мы попытаемся установить значение свойства p , другими словами, если мы вызовем метод set () свойства p , будет вызван метод setValue () экземпляра Delegate .

Oiyio
источник
7

Делегирование на имущество:

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
алексрнов
источник