Kotlin:入门lambda和高阶函数
Kotlin:入门lambda和高阶函数
让我们来看看一些常见的让新手血压高的语法糖缩写是怎么一步步被创造出来的
lambda表达式入门
首先我们要了解如何定义一个函数类型,括号内写要传入的参数类型,多个参数用逗号隔开,->
右边是返回值类型,无返回值注意写Unit
,他有点类似于void,不过Unit的话更具备泛型的一些特性,这个有机会再说。同时在表达式内部也很少会用到return返回
(String, Int) -> Unit
-
我们先来声明一个函数类型,实现传入一个参数返回一句话的简单功能:
val testFunction: (String) -> String testFunction = { name -> val saySomething = "Hello" "$saySomething $name" //返回{}内最后一行 } println(testFunction("Tommy"))
这里我们接收了一个String类型的变量并命名为name;返回一个String类型的变量,就是
{ }
包起来的内容里的最后一条。然后用等号和大括号为testFunction定义相匹配的函数代码。这里的输出结果是Hello Tommy
,很好懂。 -
其实等号可以简写,比如像这样修改到到第一行就行:
val testFunction: (String) -> String = { name -> val saySomething = "Hello" "$saySomething $name" }
-
当只有一个参数的时候,我们还能使用
it
关键字来代替该参数,从而免去为参数起名的苦恼(好耶),我们把上面的代码改改就变成了这样:val testFunction: (String) -> String = { // 删去了name -> val saySomething = "Hello" "$saySomething $it" // 原来的name改成了it,结果不变 }
-
那么非常自然地想到,传入两个或多个参数(注意现在可不能用
it
了)也应该可以的吧,比如我还想加入一个Int型变量,只需要稍微修改代码即可:val testFunction: (String, Int) -> String = { name, num -> val saySomething = "Hello" "$saySomething $name, you're No.$num" }
这里就会输出
Hello Any, you're No.3
到目前为止,都还十分好懂,接下来我们来看看,由于Kotlin优秀的类型推导机制,为我们带来了哪些缩写方法
-
首先我们来看看当没有参数的情况,当然我们直接把口号内的参数删除就好,这也是非常明显的做法:
val testFunction: () -> String = { val saySomething = "Hello" "$saySomething" }
但Kotlin的类型推断机制能够自动把
{ }
里的内容所返回的数据类型所推断出来,因此返回值的String也是可以省略的,就可以改写成以下形式:val testFunction = { val saySomething = "Hello" "$saySomething" //看这行就知道返回值是String }
好的它现在变成了一个非常精简的模样,如果是刚学java就跑来看这个故意还以为你把一个代码块赋值给了一个奇怪的东西,已经失去了常规意义上一个函数该有的模样。
-
那么有参数的情况呢?也能这样子简写吗?当然可以,不过还是有一点区别的,传入参数名和参数类型还是要声明的,比如现在我想要把上面两个参数的情况改写一下,就变成了这样:
val testFunction = { name: String, num: Int -> // 在{}括号内声明了参数名和参数类型 val saySomething = "Hello" "$saySomething $name, you're No.$num" }
lambda表达式算是入门了,那么下面来看看高阶函数
高阶函数
高阶函数的定义也十分简单,就是一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么就可以成为高阶函数。一般接收或返回的函数就会用到上述所说的lambda表达式。同样我们采取由冗杂到精简循序渐进的学法。
-
我们首先来定义一个简单的高阶函数
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int { val result = operation(num1, num2) return result }
这里我们定义了一个高阶函数num1AndNum2,传入三个参数,num1,num2,operation,其中operation还是一个传入两个整型参数,返回值也是整形的的函数类型参数,对于高阶函数num1AndNum2来说,它的返回值为
{ }
里的返回内容,也是整形。 那么怎么使用这个高阶函数呢?对于前面的两个整形参数当然就像以往的方式填入就写了,问题是operation该传入什么参数?答案其实也很明显,就是传入一个函数,我们可以像这样做:
fun plus(num1: Int, num2: Int): Int { return num1 + num2 } fun minus(num1: Int, num2: Int): Int { return num1 - num2 } fun main{ val num1 = 100 val num2 = 80 val result1 = num1AndNum2(num1, num2, ::plus) val result2 = num1AndNum2(num1, num2, ::minus) println("result1 is $result1") println("result2 is $result2") }
这里我们定义了两个参数声明和返回值声明都与operation参数完全匹配的函数,然后用
::plus
和::minus
的引用写法即可,最后的运行结果为result1 is 180
和result1 is 20
-
接下来是用lambda表达式来简写,代码如下
fun main{ val result3 = num1AndNum2(num1, num2) { n1, n2 -> n1 + n2 } val result4 = num1AndNum2(num1, num2) { n1, n2 -> n1 - n2 } println("result3 is $result3") println("result4 is $result4") }
相信有了刚才的lambda表达式入门,也可以很好地理解这一写法吧?
应用实例
我们已经有了一些基础了,现在我们来结合扩展函数的知识来模拟apply函数的功能吧!
-
我们先来定义一个高级函数()
fun StringBuilder.build(block: StringBuilder.() -> Unit):StringBuilder { block() return this }
这里我们为StringBuilder类定义了一个拓展函数build,build是一个高阶函数,接收一个无参无返回值的参数类型,然后build函数的返回值类型也是StringBuilder。
这时候我们会注意到,在函数类型前面多了一个
StringBuilder.
的语法结构,其实这才是定义高阶函数的完整语法规则,在函数类型前加上ClassName.
就表示这个函数类型是定义在哪个类当中的。这样当我们调用build函数时传入的Lambda表达式将会自动拥有StringBuilder的上下文,也就类似于apply函数的实现方式。下面我们用一个吃水果的例子说明val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape") val result = StringBuilder().build { append("Start eating fruits.\n") for (fruit in list) { append(fruit).append("\n") } append("Ate all fruits.") } println(result.toString())
这样在用法上和apply函数还是很相像的,不过我们的函数目前只能作用在StringBuilder类上,若想要和apply函数一样所用在所有类上,还得借助于Kotlin的泛式才行。