您的位置:首页 > 编程语言

《Kotlin从小白到大牛》第14章:函数式编程基石——高阶函数和Lambda表达式

2020-07-12 17:44 78 查看

第14章 函数式编程基石——高阶函数和Lambda表达式

函数式编程思想虽然与面向对象一样立即悠久,但是支持函数式编程的计算机语言不过是近几年的事情。这些语言有Swift、Python、Java 8和C++11等,作为新生的语言Kotlin也支持函数式编程。本章将介绍Kotlin语言中函数式编程最重要的基础知识——高阶函数和Lambda表达式。

14.1 函数式编程简介

函数式编程(functional programming)与面向对象编程一样都一种编程范式,函数式编程,也称为面向函数的编程。在函数式编程中一切都是函数。
函数式编程核心概念如下:
o 函数是“一等公民”:是指函数与其他数据类型是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。
o 使用表达式,不用语句:函数式编程关心的输入和输出,即:参数和返回值。在程序中使用表达式可以有返回值,而语句没有。例如:控制结构中的if和when结构都属于表达式。
o 高阶函数:函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。
o 无副作用:是指函数执行过程会返回一个结果,不会修改外部变量,这就是“纯函数”,同样的输入参数一定会有同样的输出结果。
Kotlin语言支持函数式编程,提供了函数类型、高阶函数和Lambda表达式。

14.2 高阶函数

函数式编程的关键是高阶函数的支持。一个函数可以作为另一个函数的参数,或者返回值,那么这个函数就是“高阶函数”。本节介绍一下高阶函数。

14.2.1 函数类型
Kotlin中每一个函数都有一个类型,称为“函数类型”,函数类型作为一种数据类型与数据类型在使用场景没有区别。可以声明变量,也可以作为其他函数的参数或者其他函数的返回值使用。
现有如下3个函数的定义:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.1.kt
package com.a51work6.section2

//定义计算长方形面积函数
//函数类型(Double, Double) -> Double
fun rectangleArea(width: Double, height: Double): Double { ①

return  width * height

}

//定义计算三角形面积函数
//函数类型(Double, Double) -> Double
fun triangleArea(bottom: Double, height: Double) = 0.5 * bottom * height ②

fun sayHello() { //函数类型()->Unit ③
print(“Hello, World”)
}

fun main(args: Array) {

val getArea: (Double, Double) ->Double = ::triangleArea ④
//调用函数
val area = getArea(50.0, 40.0)    ⑤
print(area)   //1000.0

}
上述代码中,函数rectangleArea和triangleArea具有相同的函数类型(Double, Double) -> Double。函数类型就是把函数参数列表中的参数类型保留下来,再加上箭头符号和返回类型,形式如下:
参数列表中的参数类型 -> 返回类型
每一个函数都有函数类型,即便是函数列表中没有参数,以及没有返回值的函数也有函数类型,如代码第③行的sayHello()函数,sayHello()函数的函数类型是()->Unit。

14.2.2 函数字面量
函数类型可以声明的变量,那么函数类型变量能够接收什么的数据呢?即函数字面量如何表示的问题,函数字面量可以有三种表示:
函数引用。引用到一个已经定义好的,有名字的函数。它可以作为函数字面量。
匿名函数。没有名字的函数,即匿名函数,它也可以作为函数字面量。
Lambda表达式。Lambda表达式是一种匿名函数,可以作为函数字面量。
示例代码如下:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.2.kt
package com.a51work6.section2

fun calculate(opr: Char): (Int, Int) -> Int {

//加法函数
fun add(a: Int, b: Int): Int {
return a + b
}

//减法函数
fun sub(a: Int, b: Int): Int {
return a - b
}

val result: (Int, Int) -> Int =
when (opr) {
'+' -> ::add                  ①
'-' -> ::sub                    ②
'*' -> {
//乘法匿名函数
fun(a: Int, b: Int):Int {       ③
return (a * b)
}
}
else -> { a, b ->(a / b) } //除法Lambda表达式    ④
}
return result

}

fun main(args: Array) {
val f1 = calculate(’+’) ⑤
println(f1(10, 5)) //调用f1变量 ⑥
val f2 = calculate(’-’)
println(f2(10, 5))
val f3 = calculate(’*’)
println(f3(10, 5))
val f4 = calculate(’/’)
println(f4(10, 5))
}
上述代码第①行和第②行是函数引用,采用“双冒号加函数名”形式引用,add和sub是两个局部函数,它们的函数引用表示方式是::add和::sub,它们可以作为函数字面量赋值给result变量。代码第③行声明匿名函数,匿名函数不需要函数名,它是一个表达式直接赋值给result变量。代码第④行采用的Lambda表达式,也可以赋值给result变量。
获得一个函数类型的变量之后如何使用呢?答案是可以把它当作函数一样调用。例如代码第⑤行val f1 = calculate(’+’)中f1是一个函数类型变量,事实上f1就是指向add函数的。代码第⑥行是调用f1函数类型变量,事实上就是在调用add函数。其他的变量以此类推,不再赘述。

14.2.3 函数作为另一个函数返回值使用
可以把函数作为另一个函数的返回值使用,那么这个函数属于高阶函数。14.2.2节的calculate函数的返回类型就是(Int, Int) -> Int函数类型,说明calculate是高阶函数。下面再介绍一个函数作为另一个函数返回值使用的示例:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.3.kt
package com.a51work6.section2

fun getArea(type: String): (Double, Double) -> Double { ①

var returnFunction: (Double, Double)-> Double             ②

when (type) {
"rect" ->   //rect 表示长方形
returnFunction =::rectangleArea    ③
else ->  //tria 表示三角形
returnFunction =::triangleArea     ④
}

return returnFunction      ⑤

}

fun main(args: Array) {

//获得计算三角形面积函数
var area: (Double, Double) ->Double = getArea("tria")            ⑥
println("底10 高13,计算三角形面积:${area(10.0,15.0)}")     ⑦

//获得计算长方形面积函数
area = getArea("rect")               ⑧
println("宽10 高15,计算长方形面积:${area(10.0,15.0)}")     ⑨

}

上述代码第①行定义函数getArea,其返回类型是(Double, Double) -> Double,这说明返回值是一个函数类型。第②行代码声明returnFunction变量,显式指定它的类型是(Double, Double) -> Double函数类型。第③行代码是在类型type为rect(即长方形)的情况下,把rectangleArea函数引用赋值给returnFunction变量,这种赋值之所以能够成功是因为returnFunction类型是(Double, Double) -> Double函数类型。第④行与第③行代码一样不再解释。第⑤行代码将returnFunction变量返回。
代码第⑥行和第⑧行调用函数getArea,返回值area是函数类型变量。第⑦行和第⑨行中的area(10,15)调用函数其参数列表是(Double, Double)。
上述代码运行结果如下:
底10 高15,计算三角形面积:75.0
宽10 高15,计算长方形面积:150.0

14.2.4 函数作为参数使用
作为高阶函数还可以接收另一个函数作为参数使用。下面来看一个函数作为参数使用的示例:
//代码文件:chapter14/src/com/a51work6/section2/ch14.2.4.kt
package com.a51work6.section2

//高阶函数,funcName参数是函数类型
fun getAreaByFunc(funcName: (Double, Double) -> Double, a: Double, b:
Double): Double { ①
return funcName(a, b)
}

fun main(args: Array) {

//获得计算三角形面积函数
var result = getAreaByFunc(::triangleArea,10.0, 15.0)               ②
println("底10 高15,计算三角形面积:$result")              ③

//获得计算长方形面积函数
result = getAreaByFunc(::rectangleArea, 10.0,15.0)         ④
println("宽10 高15,计算长方形面积:$result")              ⑤

}
上述代码第①行定义函数getAreaByFunc,它的第一个参数funcName是函数类型(Double, Double) -> Double,第二个和第三个参数都是Double类型。函数的返回值是Double类型,是计算几何图形面积。
代码第②行是调用函数getAreaByFunc,给它传递的第一个参数::triangleArea是函数引用,第二个参数是三角形的底边,第三个参数是三角形的高。函数的返回值result是Double,是计算所得的三角形面积。
第③行也是调用函数getAreaByFunc,给它传递的第一个参数::rectangleArea是函数引用,第二个参数是长方形的宽,第三个参数是长方形的高。函数的返回值result也是Double,是计算所得的长方形面积。

上述代码的运行结果如下:
底10 高15,三角形面积:75.0
宽10 高15,计算长方形面积:150.0
综上所述,比较本节与上一节的示例,可见它们具有相同的结果,都使用了函数类型(Double, Double)
-> Double,通过该函数类型调用triangleArea和rectangleArea函数来计算几何图形面积。上一节是把函数作为函数返回值类型使用,而本节是把函数作为另一个函数的参数使用。经过前文的介绍,你会发现函数类型也没有什么难理解的,与其他类型的用法一样。

14.3 Lambda表达式

14.2.2节已经使用到了Lambda表达式,Lambda表达式是一种匿名函数,可以作为表达式、函数参数和函数返回值使用,Lambda表达式的运算结果是一个函数。

14.3.1 Lambda表达式标准语法格式
Kotlin中的Lambda表达式很灵活,其标准语法格式如下:
{ 参数列表 ->
Lambda体
}
其中,Lambda表达式的参数列表与函数的参数列表形式类似,但是Lambda表达式参数列表前后没有小括号。箭头符号将参数列表与Lambda体分隔开,Lambda表达式不需要声明返回类型。Lambda表达式可以有返回值,如果没有return语句Lambda体的最后一个表达式就是Lambda表达式的返回值,如果有return语句返回值是return语句后面的表达式。

重构14.2.2节示例代码如下:
//代码文件:chapter14/src/com/a51work6/section3/ch14.3.1.kt
package com.a51work6.section3

private fun calculate(opr: Char): (Int, Int) -> Int {

return when (opr) {
'+' -> { a: Int, b: Int ->a + b }     ①
'-' -> { a: Int, b: Int ->a - b }       ②
'*' -> { a: Int, b: Int ->a * b }       ③
else -> { a: Int, b: Int ->a / b }     ④
}

}

fun main(args: Array) {
val f1 = calculate(’+’) ⑤
println(f1(10, 5)) //调用f1变量 ⑥
val f2 = calculate(’-’)
println(f2(10, 5))
val f3 = calculate(’*’)
println(f3(10, 5))
val f4 = calculate(’/’)
println(f4(10, 5))
}

calculate函数是高阶函数,它的返回值是函数类型(Int, Int) ->
Int。代码第①行~第④行分别定义了4个Lambda表达式,它们的函数类型(Int, Int) -> Int与calculate函数要求的返回类型是一致的。
代码第⑤行是调用calculate函数,返回值f1也是一个函数,这就是高阶函数。代码第⑥行是调用f1函数。
另外,calculate函数还有表示称为表达式函数体形式,代码如下:
private fun calculate(opr: Char): (Int, Int) -> Int =when (opr) {
‘+’ -> { a: Int, b: Int -> a +b }
‘-’ -> { a: Int, b: Int -> a -b }
‘*’ -> { a: Int, b: Int -> a *b }
else -> { a: Int, b: Int -> a /b }
}
比较上述代码不难发现,表达式函数体要比代码块函数体代码简洁很多。

14.3.2 使用Lambda表达式
Lambda表达式也是函数类型,可以声明变量,也可以作为其他函数的参数或者返回值使用。14.3.1节示例已经实现了Lambda表达式返回值使用,下面介绍一个Lambda表达式作为参数使用示例,示例代码如下:
//代码文件:chapter14/src/com/a51work6/section3/ch14.3.2.kt
package com.a51work6.section3

//打印计算结果函数
fun calculatePrint(n1: Int,
n2: Int,
opr: Char,
funN: (Int, Int) ->Int) {//函数类型 ①
println("$n1 $opr $n2 = ${funN(n1,n2)}")
}

fun main(args: Array) {
calculatePrint(10, 5, ‘+’, { a: Int,b: Int -> a + b }) ②
calculatePrint(10, 5, ‘-’, funN = {a: Int, b: Int -> a - b }) ③
}
代码第①行calculatePrint函数的最后一个参数是函数类型(Int, Int) -> Int。代码第②行是调用calculatePrint,第三个参数传递是的Lambda表达式。代码第③行是调用calculatePrint,第三个参数采用命名参数方式,传递是的Lambda表达式。

14.3.3 Lambda表达式简化写法
Kotlin提供了多种Lambda表达式简化写法,下面介绍其中几种。
1.参数类型推导简化
类型推导是Kotlin的强项,Kotlin编译器可以根据上下文环境推导出参数类型和返回值类型。以下代码是标准形式的Lambda表达式:
{ a: Int, b: Int -> a + b }
Kotlin能推导出参数a和b是Int类型,当然返回值也是Int类型。简化形式如下:
{ a, b -> a + b }
使用这种简化方式修改后的calculate函数代码如下:
private fun calculate(opr: Char): (Int, Int) -> Int =
when (opr) {
‘+’ -> { a, b -> a + b }
‘-’ -> { a, b -> a - b }
‘*’ -> { a, b -> a * b }
else -> { a, b -> a / b }
}
上述代码的Lambda表达式是14.3.1节示例的简化写法,其中a和b是参数。

2.使用尾随Lambda表达式
Lambda表达式可以作为函数的参数传递,如果Lambda表达式很长,就会影响程序的可读性。如果一个函数的最后一个参数是Lambda表达式,那么这个Lambda表达式可以放在函数括号之后。示例代码如下:
fun calculatePrint1(funN: (Int, Int) -> Int) { //参数是函数类型 ①
//使用funN参数
println("${funN(10, 5)}")
}

//打印计算结果函数
//ch14.3.2.kt中的calculatePrint
fun calculatePrint(n1: Int,
n2: Int,
opr: Char,
funN: (Int, Int) ->Int) {//最后一个参数是函数类型 ②
println("${n1} ${opr} n2={n2} =n2={funN(n1, n2)}")
}

fun main(args: Array) {

calculatePrint(10, 5, '+', { a, b-> a + b })//标准形式
calculatePrint(10, 5, '-') { a, b-> a - b }//尾随Lambda表达式形式      ③

calculatePrint1({ a, b -> a + b})//标准形式
calculatePrint1() { a, b -> a + b}//尾随Lambda表达式形式                ④
calculatePrint1 { a, b -> a + b}//尾随Lambda表达式,如果只有没有参数可省略括号    ⑤

}
上述代码第①行和第②行定义了两个高阶函数,它们的最后一个参数都是函数类型。代码第③行、第④行和第⑤行都是采用尾随Lambda表达式形式调用函数。由于调用calculatePrint1函数采用了尾随Lambda表达式形式,这样一来它的小括号中就没有参数了,这种情况下可以省略小括号,见代码第⑤行。

3.省略参数声明
如果Lambda表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda体中使用隐式参数it替代Lambda表达式的参数。示例代码如下:
fun revreseAndPrint(str: String, funN: (String) -> String)
{ ①
val result = funN(str)
println(result)
}

fun main(args: Array) {

revreseAndPrint("hello", {s -> s.reversed() })//标准形式           ②
revreseAndPrint("hello", {it.reversed() })//省略参数,使用隐式参数it  ③

val result1 = { a: Int ->println(a) }//不能省略参数声明             ④
val result2:(Int)->Unit = {println(it) }//可以省略参数声明          ⑤
result2(30)      //输出结果是30

}
上述代码第①行是定义反转并打印字符串高阶函数revreseAndPrint,它的第二个参数是函数类型(String) -> String)。代码第②行和第③行是调用revreseAndPrint函数,区别是代码第②行采用是标准的Lambda表达式,而代码第③行省略了参数s声明,使用it隐式变量替代。

14.3.4 Lambda表达式与return语句
Lambda表达式体中也可以使用return语句,它会使程序跳出Lambda表达式体。示例代码如下:
//代码文件:chapter14/src/com/a51work6/section3/ch14.3.4.kt
package com.a51work6.section3

//累加求和函数
fun sum(vararg num: Int): Int {

var total = 0
num.forEach {           ①
//if (it ==10) return -1   //返回最近的函数                    ②
if (it ==10) return@forEach//返回Lambda表达式函数       ③
total += it
}
return total

}

fun main(args: Array) {

val n = sum(1,2, 10, 3)
println(n)          //6

val add =label@ {     ④
val a = 1
val b = 2

return@label 10 ⑤
a + b
}
//调用Lambda表达式add
println(add()) //10

}
上述代码第①行是使用了forEach函数,它后面的Lambda表达式,如果使用代码第②行if (it == 10) return -1语句,则会返回最近的函数,即sum函数,不是返回Lambda表达式forEach。为了返回Lambda表达式则需要在return语句后面加上标签,见代码第③行,@forEach是隐式声明标签,标签名是Lambda表达式所在函数名(forEach)。也可以为Lambda表达式声明显示标签,代码第④行label@是Lambda表达式显示声明标签,代码第⑤行是使用显示标签。

14.4闭包与捕获变量

闭包(closure)是一种特殊的函数,它可以访问函数体之外的变量,这个变量和函数一同存在,即使已经离开了它的原始作用域也不例外。这种特殊函数一般是局部函数、匿名函数或Lambda表达式。
闭包可以访问函数体之外的变量,这个过程称为捕获变量。示例代码如下:
// 全局变量
var value = 10

fun main(args: Array) {
//局部变量
var localValue = 20

val result = { a: Int ->               ①
value++                           ②
localValue++                    ③
val c = a + value + localValue      ④
println(c)
}
result(30)      //输出结果是62

println("localValue = " + localValue) //输出结果是localValue = 21
println("value= " + value) //输出结果是value = 11
}
本例中的闭包是捕获value和localValue变量的Lambda表达式。代码第①行是Lambda表达式,在Lambda体中捕获变量value和localValue。代码第②行是修改全局变量value,代码第③行是修改局部变量localValue。代码第④行是读取value和localValue变量。

下面是一个局部函数示例:
fun makeArray(): (Int) -> Int { ①

var ary = 0       ②

//局部函数捕获变量
fun add(element: Int): Int {         ③
ary += element                 ④
return ary                         ⑤
}

return ::add     ⑥

}
fun main(args: Array) {

val f1 = makeArray()         ⑦
println("---f1---")
println(f1(10))//累加ary变量,输出结果是10
println(f1(20))//累加ary变量,输出结果是30
println(f1(30))//累加ary变量,输出结果是60

}
在上述代码中,第①行定义函数makeArray,它的返回值是(Int) -> Int函数类型。第②行声明并初始化变量ary,它的作用域是makeArray函数体。第③行代码定义了局部函数add,在add函数体内,第④行代码修改变量ary值。第⑤行代码是从add函数中返回变量ary。第⑥行代码是返回局部函数::add引用。
这样当在第⑦行调用的时,f1是局部函数add的一个变量。需要注意的是,f1每次调用时,ary变量作用域已经不存在,但是ary变量值都能够被保持。
上述示例也可以改为匿名函数实现,代码如下所示:
fun makeArray(): (Int) -> Int {

var ary = 0

//匿名函数形式捕获变量
return
fun(element: Int): Int {     ①
ary +=element
return ary          ②
}

}
makeArray函数返回一个匿名函数,见代码第①行。代码第②行是匿名函数返回值。比较匿名函数与局部函数,会发现Lambda表达式代码比较简洁,实现的结果完全一样。
上述示例也可以改为Lambda表达式实现,代码如下所示:
fun makeArray(): (Int) -> Int {

var ary = 0

//Lambda表达式形式捕获变量
return { element ->         ①
ary += element
ary                          ②
}

}
makeArray函数返回一个Lambda表达式,见代码第①行。代码第②行是Lambda表达式返回值,在ary是Lambda体的最后一行,它是Lambda表达式返回值,不需要return语句。比较Lambda表达式与匿名函数和局部函数,会发现Lambda表达式代码最为简洁,最后实现的结果完全一样。

14.5 内联函数

在高阶函数中参数如果是函数类型,则可以接收Lambda表达式,而Lambda表达式在编译时被编译称为一个匿名类,每次调用函数时都会创建一个对象,如果这种被函数反复调用则创建很多对象,会带来运行时额外开销。为了解决次问题,在Kotlin中可以将这种函数声明为内联函数。

14.5.1 自定义内联函数
Kotlin标准库提供了很多常用的内联函数,开发人员可以自定义内联函数,但是如果函数参数不是函数类型,不能接收Lambda表达式,那么这种函数一般不声明为内联函数。声明内联函数需要使用关键字inline修饰。
示例代码如下:
//代码文件:chapter14/src/com/a51work6/section5/ch14.5.1.kt
package com.a51work6.section5

//内联函数
inline fun calculatePrint(funN: (Int, Int) -> Int) { ①
println("${funN(10, 5)}")
}

fun main(args: Array) {
calculatePrint{ a, b -> a + b } ②
calculatePrint{ a, b -> a - b } ③
}
上述代码第①行声明了一个内联函数calculatePrint,它的参数是(Int, Int) -> Int函数类型,它可以接收Lambda表达式。代码第②行和第③行分别调用了calculatePrint函数。

14.5.2 使用let函数
在Kotlin中一个函数参数被声明为非空类型时,也可以接收可空类型的参数,但是如果实际参数如果真的为空,可能会导致比较严重的问题。因此需要在参数传递之前判断可空参数是否为非空,示例代码如下:
//代码文件:chapter14/src/com/a51work6/section5/ch14.5.2.kt
package com.a51work6.section5

fun square(num: Int): Int = num * num ①

fun main(args: Array) {
val n1: Int? = 10 //null ②
//自己进行非空判断
if (n1 != null) { ③
println(square(n1)) ④
}
}
上述代码第①行是声明一个函数square,参数是非空整数类型,该函数实现一个整数的平方运算。代码第②行是声明一个可空整数类型(Int?)变量n1,代码第③行是判断n1是否为非空,如果非空才调用,见代码第④行。
自己判断一个对象非空比较麻烦。在Kotlin中任何对象都可以一个let函数,let函数后面尾随一个Lambda表达式,在对象非空时执行Lambda表达式中的代码,为空时则不执行。
示例代码如下:
n1?.let { n -> println(square(n)) }
n1?.let { println(square(it)) }
这两行代码都是使用let函数进行调用效果是一样的,当n1非空时执行Lambda表达式中的代码,如果n1为空则不执行。n1?.let { println(square(it))
}语句是省略了参数声明,使用隐式参数it替代参数n。

14.5.3 使用with和apply函数
有时候需要对一个对象设置多个属性,或调用多个函数时,可以使用with或apply函数。与let函数类似Kotlin中所有对象都可以使用这两个函数。
示例代码如下:
//代码文件:chapter14/src/com/a51work6/section5/ch14.5.3.kt
package com.a51work6.section5

import java.awt.BorderLayout
import javax.swing.JButton
import javax.swing.JFrame
import javax.swing.JLabel
class MyFrame(title: String) : JFrame(title) {

init {
// 创建标签
val label =JLabel("Label")

// 创建Button1
val button1= JButton()                        ①

button1.text = “Button1”
button1.toolTipText = “Button1”
// 注册事件监听器,监听Button1单击事件
button1.addActionListener { label.text = “单击Button1” } ②

// 创建Button2
val button2= JButton().apply {             ③
text ="Button2"

toolTipText = “Button2”
// 注册事件监听器,监听Button2单击事件
addActionListener { label.text = “单击Button2” }
// 添加Button2到内容面板
contentPane.add(this, BorderLayout.SOUTH)
} ④

with(contentPane) { ⑤
// 添加标签到内容面板
add(label, BorderLayout.NORTH)
// 添加Button1到内容面板
add(button1, BorderLayout.CENTER)
println(height)
println(this.width)
} ⑥

// 设置窗口大小

setSize(350, 120)
// 设置窗口可见
isVisible =true
}
}

fun main(args: Array) {
//创建Frame对象
MyFrame(“MyFrame”)
}
上述代码是Swing的窗口,Swing是Java的用户图形界面介绍,Swing会在第22章介绍,本示例有图形界面组件的技术细节本暂不讨论。代码第①行和第③行分别创建两按钮对象,其中代码第①行第②行是创建并调用Button1的属性和函数,这传统的做法,由于多次调用同一个对象的属性或函数,可以使用with或apply函数,代码第③行第④行是创建并调用Button2的属性和函数,其中使用apply函数,apply函数后面尾随一个Lambda表达式,需要调用的属性和函数被放到Lambda表达式中,Lambda表达式中省略的对象名button2,例如text = "Button2"表达式说明调用的button2的text属性,apply函数中如果引用当前对象可以使用this关键字,例如contentPane.add(this,
BorderLayout.SOUTH)中的this,apply函数是有返回值的,它的返回值就是当前对象。
如果不需要返回值可以使用with函数,with函数与apply函数类似,代码第⑤行~第⑥行是使用with函数,with函数后面也尾随一个Lambda表达式,需要调用的属性和函数被放到Lambda表达式中,with函数中如果引用当前对象也是使用this关键字。

本章小结

本章主要介绍了高阶函数和Lambda表达式,读者需要理解函数式编程特点。熟悉高阶函数和Lambda表达式特点。掌握Lambda表达式标准语法,了解Lambda表达式的几个简写方式,以及尾随Lambda表达式,熟悉闭包等内容。了解什么是内联函数,以及自定义内联函数,熟悉使用let、with和apply等内联函数的使用场景。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: