'by'关键字在Kotlin中做了什么?

Mar*_*ark 40 kotlin

'by'关键字在Kotlin中做了什么?在为android开发时,我有时会遇到类似这样的东西:

var someModel: someViewModel by notNullAndObservable { vm ->
  ...
}
Run Code Online (Sandbox Code Playgroud)

我不明白'by'关键字的重要性是什么.

Jay*_*ard 50

Kotlin参考中,您将找到两种用途by,第一种是委托属性,这是您在上面使用的:

有一些常见的属性,虽然我们可以在每次需要时手动实现它们,但是一劳永逸地实现并放入库中会非常好.示例包括延迟属性:仅在首次访问时才计算值,可观察属性:侦听器收到有关此属性更改的通知,将属性存储在映射中,而不是分别存储在单独的字段中.

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

接口/类代表团是其他用途:

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

在这里,您可以将接口委托给另一个实现,因此实现类只需要覆盖它想要更改的内容,而其余的方法则委托回更全面的实现.

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

这两个都被Kotlin语言参考所涵盖,从那里开始学习该语言的基本主题.


oiy*_*yio 35

在此处输入图片说明

语法是:

val/var <property name>: <Type> by <expression>. 
Run Code Online (Sandbox Code Playgroud)

by 后面的表达式是委托

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

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

  • @oiyio 非常有用的图表和解释。谢谢 (2认同)

ale*_*nov 18

财产委托:

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"
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

textA
value = textB
Run Code Online (Sandbox Code Playgroud)

班级委托:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

property from ClassA
fun from ClassA
Run Code Online (Sandbox Code Playgroud)

参数委托:

// 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}")
    }
}
Run Code Online (Sandbox Code Playgroud)

结果:

name: John; age: 30; address: city, street; id: 5000
Run Code Online (Sandbox Code Playgroud)

  • 完美的答案... (2认同)

dan*_*ela 11

简而言之,您可以提供的关键字来理解。

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

但是,当使用by关键字时,您要说明此getter / getter&setter是在其他位置提供的(即,它已被委派),它是后面的函数提供的。

因此,您无需使用此内置的get和set方法,而是将该工作委托给某些显式函数。

一个非常常见的示例是通过惰性来实现延迟加载特性。另外,如果您使用像Koin这样的依赖注入库,您将看到许多这样定义的属性:

var myRepository: MyRepository by inject()  //inject is a function from Koin
Run Code Online (Sandbox Code Playgroud)

在类定义中,它遵循相同的原理,它定义了提供某些功能的位置,但它可以引用任何方法/属性集,而不仅仅是get和set。

class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface
Run Code Online (Sandbox Code Playgroud)

这段代码说:我是MyClass类,并且提供SomeImplementation提供的接口SomeInterface的功能。我将自己实现SomeOtherInterface(这是隐式的,因此没有实现)

  • 你的解释既简单又切题。 (5认同)
  • 对我来说,这比公认的答案更容易理解 (3认同)