目录
Lambda表达式
高阶函数
所谓高阶函数就是把函数当作参数传递或者返回值的函数。
函数类型的声明
我们来看一下实际的例子:
fun main() {
println(f(1, 2, {
a, b -> a + b }))
println(f(1, 2, {
a, b -> a - b }))
}
fun f(i: Int, j: Int, op: (Int, Int) -> Int): Int {
return op(i, j)
}
// output:3
// output:-2
这里的f函数是一个高阶函数,它有三个参数,一个是分别是i、j、和op,i和j都是Int类型,而op是一个函数类型,我们看下它的类型声明( Int, Int) -> Int
,括号里的Int
是函数类型声明的参数,-> Int
这里的Int指的是函数类型的返回值。那么这个函数参数的意义就是通过两个Int值的某些操作返回一个Int值。而这个所谓的某些操作就是我们要具体传递的方法。例如{ a, b -> a + b }
表示将两个Int值相加。{ a, b -> a - b }
表示将两个数相减。
所有的函数类型都会有一个括号括起来的参数类型列表和返回类型。比如(A, B) -> C
表示一个函数需要传递两个参数,类型分别是A和B,返回值类型是C。参数类型可以省略,但是返回值类型不能省略。如() -> Unit
。
函数类型可以额外有一个接受者类型。例如这个定义:A.(B) -> C
,表示这个函数可以被A类型调用,参数是B类型,返回值是C类型。在这个函数内部可以直接通过this
访问A类的属性或者方法,甚至直接省略this
。
函数类型也有几个特殊的例子:
(Int, Int) -> Int?
表示参数类型是Int
,返回值类型是Int?
((Int, Int) -> Int)?
表示参数类型是Int
,返回值类型是Int
,但是整个函数可以为null(Int) -> ((Int) -> Unit)
表示参数类型是Int
,返回值类型是另外一个函数类型,而这个函数类型的参数是Int
,返回值类型是Unit
(Int) -> (Int) -> Unit
的箭头优先级的顺序是从右向左,也就是它等价于(Int) -> ((Int) -> Unit)
函数类型的初始化
有以下几种方式获取一个函数类型的实例:
- 通过lambda表达式
{ a, b -> a + b }
- 通过一个匿名函数
fun(s: String): Int { return s.toIntOrNull() ?: 0 }
- 通过一个引用已经存在的声明
String::toInt
- 如果类实现了将函数类型接口,可以使用这个类的实例,例如:
class IntTransformer: (Int) -> Int {
override operator fun invoke(x: Int): Int = TODO()
}
val intFunction: (Int) -> Int = IntTransformer()
函数类型的引用
可以通过invoke方法调用,或者直接调用。举个例子:
fun main() {
val intPlus: (Int, Int) -> Int = {
a, b -> a + b }
intPlus.invoke(2,3)
intPlus(4,5)
}
如果是带接受者的函数类型,可以把接受者作为第一个参数调用或者将接受者放在括号外类似于扩展函数的方式调用。例如:
import java.lang.StringBuilder
fun main() {
// 将string复制n份
val stringPlus: String.(Int) -> String = {
it ->
var count = it
val builder = StringBuilder()
while (count-- > 0) {
builder.append(this)
}
builder.toString()
}
println("abs".stringPlus(3))
println(stringPlus("xyz",5))
println(stringPlus.invoke("mnp",4))
}
// output:
// absabsabs
// xyzxyzxyzxyzxyz
// mnpmnpmnpmnp
将Lambda表达式传递给java方法
我们知道java中的部分匿名内部类在kotlin中可以使用lambda来表示,那么lambda表达式对应的java code是不是就是一个匿名内部类呢?
我们来看几个例子:
假设我们定义了一个java 接口和设置这个接口的方法:
public interface OnClickListener {
void onClick();
}
public class HelloJava {
public void setListener(OnClickListener onClickListener){
}
}
现在在Kotlin中调用这个setListener
方法:
fun main() {
var s = "sss"
// 1
HelloJava().setListener {
println("hi") }
// 2
HelloJava().setListener {
println("hi") }
// 3
HelloJava().setListener {
print("$s") }
// 4
HelloJava().setListener(object : OnClickListener {
override fun onClick() {
println("hi")
}
})
}
1、2、3都是使用lambda的方式,而4是使用匿名内部类的方法。
来看下这四种方法对应的java code:
public static final void main() {
final ObjectRef s = new ObjectRef();
s.element = "sss";
// 1
(new HelloJava()).setListener((OnClickListener)null.INSTANCE);
// 2
(new HelloJava()).setListener((OnClickListener)null.INSTANCE);
// 3
(new HelloJava()).setListener((OnClickListener)(new OnClickListener() {
public final void onClick() {
String var1 = String.valueOf((String)s.element);
System.out.print(var1);
}
}));
// 4
(new HelloJava()).setListener((OnClickListener)(new OnClickListener() {
public void onClick() {
String var1 = "hi";
System.out.println