在为android开发时,我有时会遇到这样的情况:

var someModel: someViewModel by notNullAndObservable { vm ->
  ...
}

我不明白by关键字的意义是什么。


在Kotlin引用中,你会发现by的两种用法,第一个是委托属性,这是你上面的用法:

有一些常见的属性类型,尽管我们可以在每次需要时手动实现它们,但一次性实现并放入库中会非常好。例如惰性属性:值只在第一次访问时计算, 可观察属性:监听器得到关于该属性更改的通知, 将属性存储在一个映射中,而不是在每个单独的字段中。

在这里,您将getter/setter委托给另一个类,这个类可以完成这项工作,并且可以包含公共代码。另一个例子是,Kotlin的一些依赖注入器通过委托getter从依赖注入引擎管理的实例注册中心接收值来支持这个模型。

接口/类委托是另一个用途:

委托模式已被证明是实现继承的一个很好的替代方案,并且Kotlin支持它,它本身不需要任何样板代码。派生类可以继承接口基,并将其所有公共方法委托给指定的对象

在这里,您可以将接口委托给另一个实现,这样实现类只需要覆盖它想要更改的内容,而其余方法则委托给更完整的实现。

一个活生生的例子是Klutter Readonly/Immutable集合,它们实际上只是将特定的集合接口委托给另一个类,然后覆盖在只读实现中需要不同的任何内容。节省了大量的工作,不必手动委托所有其他方法。

这两个都在Kotlin语言参考中涵盖,从该语言的基本主题开始。


简单地说,您可以通过提供的关键字来理解。

从属性消费者的角度来看,val是具有getter (get)的东西,var是具有getter和setter (get, set)的东西。对于每个var属性,都有一个默认的get和set方法提供程序,我们不需要显式地指定。

但是,当使用by关键字时,你是在声明这个getter/getter&setter是在其他地方提供的(即它是委托的)。它是由by后面的函数提供的。

因此,不是使用这个内置的get和set方法,而是将该任务委托给一些显式函数。

一个非常常见的例子是by lazy for lazy加载属性。 另外,如果你使用的是类似Koin的依赖注入库,你会看到很多属性是这样定义的:

var myRepository: MyRepository by inject()  //inject is a function from Koin

在类定义中,它遵循相同的原则,它定义了提供函数的位置,但它可以引用任何一组方法/属性,而不仅仅是get和set。

class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface

这段代码说: 我是MyClass类,我提供SomeInterface接口的函数,这些函数是由SomeImplementation提供的。 我将自己实现SomeOtherInterface(这是隐式的,所以没有by)


财产委托:

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

语法为:

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

by后面的表达式是委托

如果我们试图访问属性p的值,换句话说,如果我们调用属性p的get()方法,则会调用Delegate实例的getValue()方法。

如果我们试图设置属性p的值,换句话说,如果我们调用属性p的set()方法,则会调用Delegate实例的setValue()方法。