匿名函数
一、通过匿名函数给标准函数制定规则
fun main() {
val total = "Mississippi".count()
val totals = "Mississippi".count({ letter ->
letter == 's'
})
println(total)
println(totals)
}
二、函数类型与隐式返回
- 匿名函数也有类型,匿名函数可以作为变量赋值给函数类型变量,就像变量一样,匿名函数就可以在代码里传递了。变量有类型,变量可以等于函数,函数也会有类型。 函数的类型,由传入的参数和返回值类型决定
- 和具名函数不一样,除了极少数情况外,匿名函数不需要return关键字来返回数据,匿名函数会隐式或自动返回函数体最后一行语句的结果。
fun main() {
//变量的类型是一个匿名函数,返回值是字符串
val blessingFunction: () -> String = {
val holiday = "National Day"
"Happy $holiday"
}
print(blessingFunction())
}
fun main() {
//变量的类型是一个匿名函数,返回值是字符串
val blessingFunction: () -> String
blessingFunction = {
val holiday = "National Day"
"Happy $holiday"
}
print(blessingFunction())
}
三、匿名函数参数
和具名函数一样,匿名函数可以不带参数,也可以带一个或多个任何类型的参数,需要带参数时,参数的类型放在匿名函数的类型定义中,参数名则放在函数定义中。
- 单个参数
fun main() {
val blessingFunction: (String) -> String = { name ->
val holiday = "New Year"
"$name,Happy $holiday"
}
println(blessingFunction("Jack"))
}
四、it关键字
- 定义只有一个参数的匿名函数时,可以使用it关键字来表示参数名,此时参数名可以不写。当你需要传入两个或多个值参,it关键字就不能用了。
fun main() {
val blessingFunction: (String) -> String = {
val holiday = "New Year"
"$it,Happy $holiday"
}
println(blessingFunction("Jack"))
}
五、类型推断
- 定义变量时,如果已把匿名函数作为变量赋值给它,就不需要显示指明变量类型了。可以自动推断出类型。
fun main() {
val blessingFunction = {
val holiday = "New Year"
"Happy $holiday"
}
println(blessingFunction())
}
- 类型推断也支持带参数的匿名函数,但为了帮助编译器更准确地推断变量类型,匿名函数的参数名和参数类型必须有。
fun main() {
val blessingFunction: (String, Int) -> String = { name, year ->
val holiday = "New Year"
"$name,Happy $holiday $year"
}
println(blessingFunction("Jack", 2022))
val blessingFunction2 = { name: String, year: Int ->
val holiday = "New Year"
"$name,Happy $holiday $year"
}
println(blessingFunction2("Jack2", 2022))
}
六、lambda
- 我们将匿名函数成为lambda,将它的定义成为lambda表达式。lambda演算是一套数理演算逻辑。在定义匿名函数时,使用了lambda演算记法。
七、定义参数是函数的函数
- 函数的参数是另外一个函数
fun main() {
//作为函数参数,传入下方函数
val getDiscountWords = { goodsName: String, hour: Int ->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
showOnBoard("笔记本", getDiscountWords)
}
//具名函数
fun showOnBoard(goodsName: String, showDiscount: (String, Int) -> String) {
val hour = (1..24).shuffled().last() //1-24,乱序,最后一个
println(showDiscount(goodsName, hour))
}
八、简略写法
- 如果一个函数的lambda参数排在最后,或者是唯一的参数,那么括住lambda值参的一对圆括号可以省略。
fun main() {
"Mississippi".count({it=='s'})
//简略写法
"Mississippi".count{it=='s'}
}
最后一个参数是lambda:
fun main() {
//作为函数参数,传入下方函数
showOnBoard("笔记本") { goodsName: String, hour: Int ->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
//具名函数
fun showOnBoard(goodsName: String, showDiscount: (String, Int) -> String) {
val hour = (1..24).shuffled().last() //1-24,乱序,最后一个
println(showDiscount(goodsName, hour))
}
九、函数内联
- lambda可以让你灵活地编写应用,但是,灵活也是要付出代价的
- 在JVM上,你定义的lambda会以对象实例的形式存在,JVM会为所有同lambda打交道的变量分配内存,这就产生了内存开销。更糟的是,lambda的内存开销会带来严重的性能问题。幸运的是,kotlin有一种优化机制叫内联,有了内联,JVM就不需要使用lambda对象实例了,因而避免了变量内存分配。哪里需要使用lambda,编译器就会将函数体复制粘贴到哪里
- 使用lambda的递归函数无法内联,因为会导致复制粘贴无线循环,编译会发出警告。
fun main() {
//作为函数参数,传入下方函数
showOnBoard("笔记本") { goodsName: String, hour: Int ->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
//具名函数 inline内联关键字
inline fun showOnBoard(goodsName: String, showDiscount: (String, Int) -> String) {
val hour = (1..24).shuffled().last() //1-24,乱序,最后一个
println(showDiscount(goodsName, hour))
}
十、函数引用
- 要把函数作为参数传给其他函数使用,出了传lambda表达式,kotlin还提供了其他方法,传递函数引用,函数引用可以把一个具名函数转换成一个值参,使用lambda表达式的地方,都可以使用函数引用。
fun main() {
//作为函数参数,传入下方函数
showOnBoard("笔记本", ::getDiscountWords)
}
//具名函数
fun showOnBoard(goodsName: String, showDiscount: (String, Int) -> String) {
val hour = (1..24).shuffled().last() //1-24,乱序,最后一个
println(showDiscount(goodsName, hour))
}
fun getDiscountWords(goodsName: String, hour: Int): String {
val currentYear = 2027
return "${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
十一、函数类型作为返回类型
- 函数类型也是有效的返回类型,也就是说可以定义一个能返回函数的函数
fun main() {
val getConfigDiscountWords = configDiscountWords() //将返回的函数赋值给变量
println(getConfigDiscountWords("沐浴露")) //执行返回的函数
}
fun configDiscountWords(): (String) -> String {
val currentYear = 2027
val hour = (1..24).shuffled().last()
//返回一个函数 可以共享作用域
return { goodsName: String ->
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
十二、闭包
- 在Kotlin中,匿名函数能修改并引用定义在自己的作用域外的变量,匿名函数引用着定义自身的函数里的变量,Kotlin中的lambda就是闭包。
- 能接收函数或者返回函数的函数又叫做高级函数,高级函数广泛应用于函数式编程当中。
- 匿名函数就是lambda,lambda就是闭包
十三、lambda与匿名内部类
- 为什么要在代码中使用函数类型?函数类型能让开发者少写模式化代码,写出更灵活的代码。Java8支持面向对象编程和lambda表达式,但不支持将函数作为参数传给另外一个函数或变量,不过Java 的替代方案是匿名内部类。