Kotlin学习2-匿名函数

83 阅读3分钟

匿名函数

一、通过匿名函数给标准函数制定规则

fun main() {
    val total = "Mississippi".count()
    val totals = "Mississippi".count({ letter ->
        letter == 's'
    })
    println(total)
    println(totals)
}

二、函数类型与隐式返回

  1. 匿名函数也有类型,匿名函数可以作为变量赋值给函数类型变量,就像变量一样,匿名函数就可以在代码里传递了。变量有类型,变量可以等于函数,函数也会有类型。 函数的类型,由传入的参数和返回值类型决定
  2. 和具名函数不一样,除了极少数情况外,匿名函数不需要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 的替代方案是匿名内部类