Android---Kotlin 学习003

*匿名函数

定义时不取名字的函数,我们称之为匿名函数。匿名函数通常整体传递给其他函数,或者从其他函数返回。匿名函数对 Kotlin 来说很重要,有了它,我们能够根据需要制定特殊规则,轻松定制标准库里的内置函数。

示例:

fun main(){
    val count = "Mississippi".count()
    println(count)

    val countS = "Mississipi".count({letter ->
        letter == 's'
    })
    println(countS)
}

 String.count() 中 count() Kt 中自带的 API,可以统计字符串中字符的个数。而匿名函数就可以给这些自带的标准函数(API)制定一些特殊的规则。比如,我们上面统计字符 's' 出现的次数,就可以在 count() 的括号里传一个匿名函数,即 "{letter -> letter == 's'}"

函数类型与隐式返回

匿名函数也有类型,匿名函数可以当作变量赋值给函数类型变量,就像其他变量一样,匿名函数就可以在代码里传递了。变量有类型,变量可以等于函数,函数也会有类型函数的类型,由传入的参数和返回值类型决定

具名函数不一样,除了极少数情况外,匿名函数不需要 return 关键字来返回数据,匿名函数会隐式或自动返回函数体最后一行语句的结果

示例:

fun main(){
    // 变量的类型是一个匿名函数,即 冒号 后面的部分
    val blessingFunction : () -> String = {
        val holiday = "New Year."
        "Happy $holiday"
    }
    println(blessingFunction)
}

解释说明:blessingFunction 就是一个变量,这个变量的类型是一个函数的类型。因为当我们定义一个变量时一般是: val blessingFunction : String,所以冒号“:” 后面的内容就是它的类型,即这里一个函数 () -> String(这个函数无参,返回值为 String)。只要是任意一个匿名函数,它没有传入参数,返回类型是 String 类型的,都可以等于 blessingFunction 这个变量。而这里我们又用一个匿名函数来赋值,即 

{
    val holiday = "New Year."
    "Happy $holiday"
}

这个匿名函数没有参数,且返回类型为最后一行的结果,即 String。

匿名函数参数

和具名函数一样,匿名函数可以不带参数,也可以带一个或多个任何类型的参数。需要带参数时,参数的类型放在匿名函数的类型定义中,参数名则放在函数定义中。

示例1:一个 Int 参数

fun main(){
    
    val number : (Int) -> Int = {original ->
        val num = 1
        num + 1 + original
    }
    println("The result number is " + number(2))
}

解释说明:

示例2:两个参数,一个 String 的 name,一个 Int 的 age

fun main(){
    val person : (String, Int) -> String = {name, age ->
        "His name is $name, and age is $age"
    }
    println(person("HL", 17))
}

it 关键字

定义只有一个参数的匿名函数时,可以使用 it 关键字来表示参数名。当你需要传入两个参数时,it 关键字就不能用了。

示例:用 it 代替 original

类型推断

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

省略了 :() -> String 。

\bullet 类型推断也支持带参数的匿名函数,但为了帮助编译器更准确地推断变量类型,匿名函数的参数名和参数类型必须有

lambda 

我们将匿名函数称为 lambda,将它的定义称为 lambda 表达式,它返回的数据称为 lambda 结果。为什么叫 lambda?lambda 也可以用希腊字符λ表示,是 lambda 演算的简称,lambda 演算是一套数理演算逻辑,由数学家 Alonzo Church(阿隆佐.丘齐)于20世纪30年代发明,在定义匿名函数时,使用了 lambda 演算记法

定义参数是函数的函数

函数的参数是另外一个函数。可以理解为把一个匿名函数作为一个具名函数的参数传入。

简略写法

如果一个函数的 lambda 参数排在最后,或者是唯一的参数,那么括住 lambda 值参的一对圆括号就可以省略。如下代码所示:在编译器里,黄线部分就是提示我们可以省略圆括号。因为只有一个参数,且还是 lambda 

黄线消失

示例2:

上面代码的最后一个参数是 lambda, 那么括住 lambda 值参的圆括号就不需要了,因为这里 lambda 不是唯一的参数,所以正确的写法是放到圆括号外面,如下代码所示

示例3:如果只有一个参数,无论是普通参数,还是 lambda 参数都可以省略圆括号

注意:上面代码的省略写法很容易让人误解、看不懂。所以需要多理解 Kt 里的这种简略写法。

函数内联

lambda 可以让你更灵活地编写应用,但是,灵活也是要付出代价的。在 JVM 上,你定义的 lambda 会以对象实例的形式存在,JVM 会为所有同 lambda 打交道的变量分配内存,这就产生了内存开销。更糟的是,lambda 的内存开销会带来严重的性能问题。幸运的是,kotlin 有一种优化机制叫内联,有了内联,JVM 就不需要使用 lambda 对象实例了,因而避免了变量内存分配。哪里需要使用 lambda,编译器就会将函数体复制粘贴到哪里。使用 lambda 的递归函数无法内联,因为会导致复制粘贴无限循环,编译会发出警告。

函数引用

要把函数作为参数传给其他函数使用,除了传 lambda 表达式,kotlin 还提供了其他方法,传递函数引用函数引用可以把一个具名函数转换成一个值参,使用 lambda 表达式的地方,都可以使用函数引用。

示例:

解释说明:personInfo 是一个具名函数,而 getPerson 的第二个参数是匿名函数,所以通过函数引用(::函数名)就可以把一个具名函数传进去。

函数类型作为返回类型

函数类型也是有效的返回类型,也就是说可以定义一个能返回函数的函数

示例:

fun main(){
    val personInfo = getPersonInfo()
    println(personInfo("HL", 17))
}
fun getPersonInfo() : (String, Int) -> String {
    val sex = "男"
    return {name : String, age : Int ->
        "His name is $name, and he is $age years old and $sex."
    }
}

解释说明:

注意:参数的个数和类型要对应。

闭包

在 kotlin 中,匿名函数能修改并引用定义在自己的作用域之外的变量,匿名函数引用着定义自身函数的变量,Kotlin 中的 lambda 就是闭包。前面提到,匿名函数就是 lambda,lambda 就是闭包

能接收函数或者返回函数的函数又叫高级函数,高级函数广泛应用于函数式编程当中。

lambda 与匿名内部类

为什么要在代码中使用函数类型?函数类型能让开发者少写模式化代码,写出更灵活的代码。Java 8 支持面向对象编程和 lambda 表达式,但不支持将函数作为参数传给另一个函数或变量,不过 Java 的替代方案是匿名内部类

示例:Java 通过匿名内部类传递函数

这段代码还没有用到 Java 的匿名内部类,而是通过我们通常熟悉的,写一个 MyPerson 类继承自 PersonInfo 接口,然后在 main() 方法中调用 getPersonInfo 时传一个 MyPerson 对象作为参数。

解释说明:这段代码相比较上面的传统写法,不再需要一个 MyPerson 类来实现 PersonInfo 接口。在 调用 getPersonInfo() 方法时,直接 new PersonInfo 作为参数传递。 这种写法中,PersonInfo 接口也是有实现类的,只是它的实现类的名字是匿名的,所以叫做匿名内部类

上面 Java 中两种将函数作为参数的写法,都有模式化代码,即写接口的那一部分。

而在 Kt 中则完全不需要了,通过匿名函数即可实现,例如上面提到的定义参数是函数的函数、简略写法部分。 

猜你喜欢

转载自blog.csdn.net/qq_44950283/article/details/134769156