Skip to content

lambda函数式编程

lambda表达式结构是:{参数名: 参数类型, 参数名: 参数类型 … -> 函数体}(参数1, 参数2, …)

如我们要找出字符串数组的最长的那个单词,可以写成:

kotlin
fun main() {
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    var maxLengthFruit = ""
    for (fruit in list) {
        if(fruit.length > maxLengthFruit.length) {
            maxLengthFruit = fruit
        }
    }
    println(maxLengthFruit)
}

如果我们想写成lambda表达式的话,可以用maxBy这个函数,该函数工作原理是接收一个lambda表达式,根据传入的条件来找到最大值。那么上述的代码就可以写成:

kotlin
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val lambda = {fruit: String -> fruit.length}
val maxLengthFruit = list.maxBy(lambda)

然后对他进行简化,首先maxBy里面可以直接传lambda参数:

kotlin
val maxLengthFruit = list.maxBy({fruit: String -> fruit.length})

然后Kotlin还规定,当lambda参数是函数最后一个参数时,可以将{}放在()后面:

kotlin
val maxLengthFruit = list.maxBy(){fruit: String -> fruit.length}

如果lambda参数是函数唯一参数的话,还可以直接省略():

kotlin
val maxLengthFruit = list.maxBy{fruit: String -> fruit.length}

然后加上Kotlin的优秀推导类型的机制,可以直接省略类型,然后当参数只有一个时,可以直接用it代替:

kotlin
val maxLengthFruit = list.maxBy{it.length}

这样一步步推导就可以推导出最简形式了。

map函数

map函数非常常用,可以把集合的每个元素都映射成另一个值,规则则是由接受的lambda表达式决定。比如我们要将水果名变成大写:

kotlin
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.map{it.toUpperCase()}
for(fruit in newList) {
    println(fruit)
}

运行结果就是:

APPLE
BANANA
ORANGE
PEAR
GRAPE
WATERMELON

filter函数

filter函数可以过滤集合中的数据,也同样接受lambda表达式。,可以搭配map函数使用:

kotlin
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.filter{it.length < 5}
				  .map{it.toUpperCase{}}
for(fruit in newList) {
    println(fruit)
}

运行结果就是PEAR了。

Java函数式API使用

当我们用Kotlin调用Java方法时,如果这个方法只接受一个Java单抽象方法接口参数就可以换成函数式API。单抽象方法接口指这个接口中只有一个待实现的方法。比如线程中的Runnable接口,就只有一个run()方法:

java
public interface Runnable {
    void run();
}

我们在Thread类中接受一个Runnable参数:

java
new Thread(new Runnable() { //使用匿名内部类
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}).start();

在Kotlin中,没有new这个关键字,改成了object关键字:

kotlin
Thread(object : Runnable {
    override fun run() {
        println("Thread is running")
    }
}).start()

然后这个Thread类的写法符合函数API的形式,故可以简化:

kotlin
Thread(Runnable {
    println("Thread is running")
}).start()

然后如果Java方法参数列表只有一个Java单抽象方法接口参数,可以直接对接口名省略:

kotlin
Thread({
    println("Thread is running")
}).start()

然后根据上面的简化还可以将()省略:

kotlin
Thread{
    println("Thread is running")
}.start()

同样还有最常用的点击事件接口OnClickListener,也符合函数式API的形式:

java
botton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        
    }
});

Kotlin中则可以简化成:

Kotlin
button.setOnClickListener {
    
}

就可以精简很多代码了。

let,with,apply

这三个函数均提供了函数API编程接口,即能接受lambda表达式

let

kotlin
obj.let {obj2 ->
	//业务逻辑
}

在上述代码中会自动将obj参数传给obj2,为了防止重名才取不同的名字

并且当只有一个参数时,可以用it代替。如现在有一个study类,类中有readBooks(),doHomework()两个方法,可以变成:

kotlin
fun doStudy(study: Study) {
    study.let {
        it.readBooks()
        it.doHomework()
    }
}

通常在判空时使用

with

with函数接收两个参数,第一个参数是任意类型的对象,第二个参数是lambda表达式,在lambda表达式中会提供第一个参数的上下文,并用最后一行作为返回值。如现在一个水果列表,要依次吃水果:

kotlin
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val builder = StringBuilder()
builder.append("Start eating fruits.\n")
for(fruit in list) {
    builder.append(fruit).append("\n")
}
val result = builder.toString()
println(result)

with方法就可以改成:

kotlin
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val result = with(StringBuilder()) {
    append("Start eating fruits.\n")
    for(fruit in list) {
        append(fruit).append("\n")
    }
    toString()
}
println(result)

另外,还有个run函数和这个类似,只不过run函数是在某个对象的基础上使用,同样接受lambda表达式,其他则是一样的,最后一行返回值:

kotlin
val result = obj.run {
    //上下文
    "value"
}

apply

apply函数也很类似,只不过他不会返回一个值,而是自动返回调用对象本身:

ko
val resullt = obj.apply {
	//上下文
}
// result == obj

比如SharedPreference中就可以使用apply简洁的添加数据。