用于指定该参数接收一个函数或者一个 lambda 表达式。
声明
用小括号将参数类型括起来,后跟 ->,最后是返回值类型。如下:
(Int,String, Int) -> Unit
它表示该函数接收三个参数,类型分别是 int,string,int,返回值类型为 Unit。因此,下面定义的函数可以匹配上述的函数类型:
fun demo(a: Int, b: String, c: Int)
函数类型的参数可以接收一个 labmda 或 一个函数成员引用,不能直接将函数名传入其中:
fun main(args: Array<String>) {
test(::demo)
test { a, b, c ->
println("$a,$b,$c")
}
}
fun test(action: (Int, String, Int) -> Unit) {
action(3, "aaa", 2)
}
fun demo(a: Int, b: String, c: Int) {
println("$a,$b,$c")
}
函数类型接口
函数类型是一种类型,因此函数类型可以做为父类,可以定义实现了某个函数类型的类。
函数类型的子类,必须重写 invoke 方法。该方法的参数类型是函数类型的参数类型,返回值类型是函数类型的返回值类型:
fun main(args: Array<String>) {
val t = Test()
println( t("xxx")) // true
println(t("yii")) // false
}
class Test : (String) -> Boolean {
override fun invoke(p1: String): Boolean = p1.contains("xx")
}
在编译后,函数类型会转成 Function 的子类,而 Function 中定义了 invoke 方法,所以子类必须实现 invoke 方法。
而且根据 invoke 约定,可直接使用像调用函数一样调用子类实例。如上述的 t("xxx")。
实质
函数类型在编译后都会转成 FunctionN<P1,P2,...,PN,R> 的子类。其中 P1 到 PN 表示函数类型的参数类型,R 表示返回值类型。因此,(T)->R 可以认为是 Function1 类的 kt 写法,两者等价。
默认值
函数类型也是一种类型,因此可以用作函数的形参类型。
kt 的函数中,形参可以指定默认值。因此函数类型的形参也可以指定默认值,其值可以是成员引用,也可以是 lambda:
fun main(args: Array<String>) {
println(demo())
println(demo2())
}
fun test() = "aa"
fun demo(a: () -> String = { "lambda" }) = a()
fun demo2(a: () -> String = ::test) = a()
作为参数,表达式有可能为 null,只要对应的变量声明为可空类型。而 java 调用含 lambda 的函数时,在对应的位置上传递的是 FunctionN 的实例。 而 FunctionN 中定义了 invoke 方法,因此可以 结合 FunctionN 与安全调用,避免对参数的空判断。
fun test(a:((Int)->String)?){
if(a != null)
println(a(2))
}
fun test2(a:((Int)->String)?){
println(a?.invoke(3)) // 直接调用 invoke 方法,相当于执行了表达式
}
test() 方法中进行空判断,然后调用;test2 中,使用安全调用 invoke 方法。
在 java 中使用
在 java 中调用含有函数类型参数的函数时,需要显式地传入一个 FunctionN 的实例。
一个函数类型的变量是 FunctionN<> 接口的一个实现。其中 N 表示函数需要的参数个数,泛型依次为参数类型及返回值类型。
FunctionN 中有一个 invoke 方法,该方法中可以写入 lambda 的主体。
如下,调用函数类型为 (Int)->Unit
的 test 函数时,需要传入一个 Function1 的实例。因为表达式需要返回一个 Unit 类型的实例,该类型的实例只有 Unit.INSTANCE 一个,因此在 Java 中调用时需要显式返回:
DemoKt.test(new Function1<Integer, Unit>() {
@Override
public Unit invoke(Integer integer) {
return Unit.INSTANCE;
}
});
网友评论