• [Kotlin学习] 5. 匿名函数与函数


    第五章 匿名函数与函数

    5.1 匿名函数

    fun main(args: Array<String>) {
        println({
            val currentYear = 2022
            "Welcome to SimVillage, Mayor! (copyright $currentYear)"
        }())
    }
    

    定义函数就是把表达式或语句放在一对花括号里

    在花括号后面跟上一对空的圆括号,表示调用匿名函数

    5.1.1 函数类型

    匿名函数也有类型,叫做函数类型(function type)

    匿名函数可以当作变量赋值给函数类型变量

    把匿名函数赋值给变量:

    fun main(args: Array<String>) {
        val greetingFunction: () -> String = {
            val currentYear = 2022
            "Welcome to SimVillage, Mayor! (copyright $currentYear)"
        }
        println(greetingFunction())
    }
    

    : () -> String 表示变量存储的是一个函数,这个函数不需要参数,且返回值是 String

    5.1.2 隐式返回

    隐式返回:匿名函数会隐式或自动返回函数体最后一行语句的结果

    和具名函数不一样,除了极少数的情况外,匿名函数不需要 return 关键字来返回数据

    匿名函数不用 return 关键字的原因:编译器不知道返回数据究竟是来自调用匿名函数的函数,还是函数本身

    5.1.3 函数参数

    匿名参数也可以带一个或多个任何类型的参数

    fun main(args: Array<String>) {
        val greetingFunction: (String) -> String = { playerName ->
            val currentYear = 2022
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
        println(greetingFunction("Guyal"))
    }
    

    在匿名函数体内,左花括号后面,写上 String 类型的参数名,后面再跟上一个箭头符号

    5.1.4 it 关键字

    定义只有一个参数的匿名函数时,可以使用 it 关键字来表示参数名

    fun main(args: Array<String>) {
        val greetingFunction: (String) -> String = {
            val currentYear = 2022
            "Welcome to SimVillage, $it! (copyright $currentYear)"
        }
        println(greetingFunction("Guyal"))
    }
    

    5.1.5 多个参数

    it 关键字只适用于一个参数的情况,如果有多个参数,需要使用命名参数

    fun main(args: Array<String>) {
        val greetingFunction: (String, Int) -> String = { playerName, numBuildings ->
            val currentYear = 2022
            println("Adding $numBuildings houses")
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
        println(greetingFunction("Guyal", 2))
    }
    

    类型推断

    定义一个变量时,如果已把匿名函数作为变量赋值给它,就不需要显示指明变量类型

    val greetingFunction = {
        val currentYear = 2022
        "Welcome to SimVillage, Mayor! (copyright $currentYear)"
    }
    

    类型推断也支持带参数的匿名函数,但是匿名函数必须有参数名和参数类型

    fun main(args: Array<String>) {
        val greetingFunction = { playerName: String, numBuildings: Int ->
            val currentYear = 2022
            println("Adding $numBuildings houses")
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
        println(greetingFunction("Guyal", 2))
    }
    

    5.3 定义参数是函数的函数

    术语:

    • lambda: 匿名函数
    • lambda 表达式: 匿名函数的定义
    • lambda 结果: 匿名函返回的数据

    函数也可作为函数参数

    fun main(args: Array<String>) {
        val greetingFunction = { playerName: String, numBuildings: Int ->
            val currentYear = 2022
            println("Adding $numBuildings houses")
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
        runSimulation("Guyal", greetingFunction)
    }
    
    fun runSimulation(playerName: String, greetingFunction: (String, Int) -> String) {
        val numBuildings = (1..3).shuffled().last() // Randomly selects 1, 2, or 3
        println(greetingFunction(playerName, numBuildings))
    }
    

    简略语法

    如果一个函数的 lambda 参数排在最后,那么括住 lambda 值参的一堆圆括号就可以省略

    "Mississippi".count({ it == 's' })
    

    就可以简写成

    "Mississippi".count { it == 's' }
    

    同样 runSimulation 也可使用简略语法传入 lambda 值参:把非函数类型的值放在圆括号内,把函数值参放在括号外

    fun main(args: Array<String>) {
        runSimulation("Guyal") { playerName, numBuildings ->
            val currentYear = 2022
            println("Adding $numBuildings houses")
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
    }
    
    fun runSimulation(playerName: String, greetingFunction: (String, Int) -> String) {
        val numBuildings = (1..3).shuffled().last() // Randomly selects 1, 2, or 3
        println(greetingFunction(playerName, numBuildings))
    }
    

    5.4 函数内联

    在 JVM 上,lambda 会以对象实例的形式存在。JVM 会为所有同 lambda 打交道的变量分配内存,这样就产生了内存开销

    Kotlin 中可以通过内联解决 lambda 引起的内存开销问题,避免变量内存分配

    使用内联方法优化 lambda: 以 inline 关键字标记使用 lambda 的函数即可

    inline fun runSimulation(playerName: String, greetingFunction: (String, Int) -> String) {
        val numBuildings = (1..3).shuffled().last() // Randomly selects 1, 2, or 3
        println(greetingFunction(playerName, numBuildings))
    }
    

    有了 inline 关键字后,调用 runSimulation 函数就不会使用 lambda 对象实例了:哪里需要使用 lambda,编译器就会将函数体复制粘贴到哪里

    使用 lambda 的递归函数无法内联,因为内联函数会让复制粘贴函数体的行为无限循环

    5.5 函数引用

    除了传 lambda 表达式,Kotlin 还提供了“传递函数引用”方法来讲函数作为参数传给其他函数使用

    使用 :: + 函数名称操作符获得函数引用

    5.6 函数类型作为返回值

    闭包:使用定义自己的外部函数的变量的函数

    Kotlin 中的 lambda 是闭包

    fun configureGreetingFunction(): (String) -> String {
        val structureType = "hospitals"
        var numBuildings = 5
        return { playerName: String ->
            val currentYear = 2022
            numBuildings += 1
            println("Adding $numBuildings $structureType")
            "Welcome to SimVillage, $playerName! (copyright $currentYear)"
        }
    }
    

    5.7 深入学习:Kotlin 中的 lambda 就是闭包

    在 Kotlin 中,匿名函数能修改并引用定义在自己的作用域之外的变量。这表明,匿名函数引用着定义自身的函数里的变量

    runSimulation 中调用 println 两次

    fun runSimulation() {
        val greetingFunction = configureGreetingFunction()
        println(greetingFunction("Guyal"))
        println(greetingFunction("Guyal"))
    }
    

    输出为:

    Adding 6 hospitals
    Welcome to SimVillage, Guyal! (copyright 2022)
    Adding 7 hospitals
    Welcome to SimVillage, Guyal! (copyright 2022)
    

    numBuildings 变量的值从 6 增加到了 7

    5.8 深入学习:lambda 与匿名内部类

    使用函数类型的好处:函数类型能让开发者少写模式化代码,写出更为灵活的代码

    Java 8 支持面向对象编程和 lambda 表达式,但不支持讲函数作为参数传给另一个函数或变量。不过 Java 的替代方式是匿名内部类——定义在类中,用来实现某个方法的无名类

    Greeting greeting = (palyerName, numBuildings) -> {
        int currentYear = 2022;
        System.out.println("Adding " + numBuildings + " houses");
        return "Welcom to SimVillage, " + playerName + "! (copyright " + currentYear + ")";
    };
    
    public interface Greeting {
        String greet(String playerName, int numBuildings);
    }
    
    greeting.greet("Guyal", 6);
    

    这种方法和 Kotlin 传递 lambda 表达式差不多,但是 Java 需要一个命名接口或类来代表 lambda 定义的函数

  • 相关阅读:
    Android OpenGL ES 2.0 (四) 灯光perfragment lighting
    Android OpenGL ES 2.0 (五) 添加材质
    冒泡排序函数
    javascript object 转换为 json格式 toJSONString
    Liunx CentOS 下载地址
    jquery 图片切换特效 鼠标点击左右按钮焦点图切换滚动
    javascript 解析csv 的function
    mysql Innodb Shutdown completed; log sequence number解决办法
    Centos 添加 yum
    javascript 键值转换
  • 原文地址:https://www.cnblogs.com/huihao/p/15810767.html
Copyright © 2020-2023  润新知