【kotlin代码又看不懂了】你知道block: Person.() -> Unit是什么吗?

1,952 阅读1分钟

最近在Kotlin代码中,我经常看到这样的写法:

interface Person {
    fun saySomething()
}
​
class Adult: Person {
    var name = ""
    override fun saySomething() {
        println("Hello world!")
    }
​
    fun weirdSay(block: Person.() -> Unit) {
        println("包装一下")
        this.block()
    }
    private fun sayMore() {
        println("私有方法")
    }
    fun answer(question: String) {
        println("$question 的答案")
    }
}
​
fun main() {
    val adult = Adult()
    adult.weirdSay {
        saySomething()
//        sayMore() 无法找到私有方法//        answer("问题") 无法找到
    }
}

输出为

包装一下
Hello world!

注意看main函数,这里调用了adult的weirdSay,传入一个lambda表达式,传入的居然是adult自己的saySomething,那是因为block: Person.() -> Unit语法

() -> Unit是Kotlin定义不接收任何参数并且不返回任何东西的函数的方式。

并且有一种方法可以指定该函数将在何处被调用,因此有了Person.()部分。

一个参数类型为 Person.() -> Unit 的 Lambda 表达式,它可以访问和修改 Person 类型的属性和方法,具体含义是:为“给我一个在Person上调用的块。它必须不接收任何参数,并且不返回任何东西。”

显然,这是一种定义即时扩展函数的方式。这允许你定义在类内部运行但是从类外部定义的函数。

也就是说,当我们需要在业务中用到某个类内部的方法时,我们可以给这个类内部增加一个类似于 fun weirdSay(block: Person.() -> Unit) {的方法,对需要调用的方法进行相应的处理,然后在外面直接调用weirdSay就行了,同时,在外部也可以访问或者修改属性

class Person(var name: String) {
    fun say() {
        println("My name is $name")
    }
    fun weirdSay(block: Person.() -> Unit) {
        println("包装一下")
        block()
    }
}
​
​
fun main() {
    val person = Person("Tom")
    person.weirdSay {
        name = "Jerry"
        say()
    }
}

输出为

包装一下
My name is Jerry

这样子,多数情况下,就可以在这一扩展函数中自由的像在person类中调用自己一样,比如

interface Person {
    fun saySomething()
    fun sayMore() 
}
class Adult: Person {
    override fun saySomething() {
        println("Hello world!")
    }
​
    fun weirdSay(block: Person.() -> Unit) {
        println("包装一下")
        this.block()
    }
    override fun sayMore() {
        println("sayMore")
    }
​
}
​
fun main() {
    val adult = Adult()
    adult.weirdSay {
        saySomething()
        sayMore()
    }
}

增加一个sayMore方法,也可以继续调用,weirdSay{}内部就像person的内部一样(除了private不能访问)

学习到了小技巧,就赶紧用到项目中吧!