1. 前言
上篇文章介绍了函数类型及函数字面值,本文将在此基础上进行拓展,主要介绍带接收者类型的函数类型及函数字面值。
2. 带接收者类型的函数类型(function type with receiver)
2.1 示例和语法
让我们来看以下示例。
fun Int.square() = this * this
val squsreFun: Int.()->Int = Int::square
其中,Int.()->Int就是带接收者类型的函数类型,和函数类型一样,它表示的是一种类型。其语法有以下特点。
- 开头是接收者的类型,例如上例中的Int,其后是一个点,点后是小括号。
- 小括号内是参数类型和参数名,其中参数名可以省略。
- 箭头后是返回值,如果返回值为Unit,不能省略。
2.2 接收者参数化
如果一个函数带有接收者,那这个接收者可以作为第一个参数传递给函数,这两种函数是等价的。示例如下:
class FunWithReceiver {
fun innerFun(param: Int): Int {
println("innerFun $param")
return 1
}
}
fun firstParam(receiver: FunWithReceiver, x: Int): Int {
println("firstParam $x")
return 1
}
fun receiverTest() {
val obj = FunWithReceiver();
val fun1: FunWithReceiver.(Int)->Int = FunWithReceiver::innerFun
val fun2: FunWithReceiver.(Int)->Int = ::firstParam
val fun3: (FunWithReceiver, Int)->Int = FunWithReceiver::innerFun
val fun4: (FunWithReceiver, Int)->Int = ::firstParam
}
可以看到,接收者既可以放在括号里面作为第一个参数,也可以放在括号前面。
如果未显示的指定变量为带接收者的函数类型,即使使用带接收者的函数字面值对其进行赋值,该变量的类型也是不带接收者的普通函数类型。示例如下:
class FunWithReceiver {
fun innerFun(param: Int): Int {
return 1
}
}
fun firstParam(receiver: FunWithReceiver, x: Int): Int {
return 2
}
fun receiverTest() {
val receiver = FunWithReceiver()
val local1: FunWithReceiver.(Int) -> Int = ::firstParam
receiver.local1(1) //正确
local1(receiver, 1) //正确,如果函数类型是带接收者的函数,接收者可以作为第一个参数传入
val local2: (FunWithReceiver, Int) -> Int = FunWithReceiver::innerFun
receiver.local2(1) //错误,非带接收者的函数类型,不能以接收者加点的方式打开
local2(receiver, 1) //正确,如果函数类型是带接收者的函数,接收者可以作为第一个参数传入
val local3 = FunWithReceiver::innerFun
receiver.local3(1) //错误,默认为非带接收者的函数类型,不能以接收者加点的方式打开
local3(receiver, 1) //正确
}
如果变量声明为带接收者的函数类型,其可以使用带接收者的函数类型和普通函数类型两种方式调用该变量,如local1。
如果变量声明为普通函数类型,则只能用普通函数方式调用该变量,即使其被赋予带接收者函数类型的值,例如local2。
默认情况下,变量被推导出普通函数类型,例如local3。
3. 带接收者的函数类型实例化
同函数类型类似,带接收者的函数类型实例化有如下几种方式:
- 使用函数字面值进行赋值。函数字面值的具体含义下文会详细介绍。函数字面值包括两种,带接收者的lambda表达式和带接收者的匿名函数,其示例如下:
a) 带接收者的lambda表达式:
val squareFun: Int.()->Int = { this * this }
注意该lambda表达式中的this是隐式接收者,其值即接收者的值。
b) 带接收者的匿名函数:
val squareFun: Int.()->Int = fun Int.() = this * this
注意该匿名函数中的this也是隐式接收者。
- 使用一个已存在声明成员函数或者扩展函数的可调用引用,例如:
class FunWithReceiver {
fun innerFun(): Int {
return 1
}
}
fun FunWithReceiver.outerFun(): Int {
return 2
}
fun receiverTest() {
val local1: FunWithReceiver.() -> Int = FunWithReceiver::innerFun
val local2: FunWithReceiver.() -> Int = FunWithReceiver::outerFun
}
4. 带接收者的函数字面值(function literals with receiver)
和函数字面值类似,带接收者的函数字面值也包括两种,lambda表达式及匿名函数。
4.1 带接收者的lambda表达式
带接收者的lambda表达式的示例如下:
val squareFun: Int.()->Int = { this * this }
带接收者类型的lambda表达式与普通lambda表达式的区别为带接收者的lambda表达式中可以用this调用隐式接收者,而普通的不行,如上例所示。另外,变量类型必须显示的声明。
4.2 带接收者的匿名函数
带接收者的匿名函数的示例如下:
val squareFun: Int.()->Int = fun Int.() = this * this
其与普通的匿名函数的区别是它可以使用this调用隐式接收者。另外,和带接收者的lambda表达式不一样,变量的类型可以直接推导出来,因此不需要显示指出。因此上式可以简写为:
val squareFun = fun Int.() = this * this
5. 小结
本文主要介绍了带接收者的函数类型以及函数字面值的语法和示例,重点讲述了带接收者函数和普通函数之间的关联,以及两种常见的带接收者的函数字面值:带接收者的lambda表达式及匿名函数。
6. 参考文档
Programmer Dictionary: Function literal with receiver vs Function type with receiver
High-order functions and lambdas