Kotlin学习笔记之函数

108 阅读4分钟
package com.example.firstkotlin.koltin

/**
 * 项目名称 firstKotlin
 * 创建时间 12/26/21 3:27 PM
 *
 **/
class Function {

}

fun main(){
    doSomeThing("小明")

    fix()
    fix("小红")

    // 在函数参数特别多的时候,可以采用这种方式 on 12/26/21 3:47 PM
    fix(name = "小亮",age = 22)
    // 如果采用具名函数参数,可以不用关心参数顺序 on 12/26/21 3:47 PM
    fix(age = 22,name = "小亮")

}
kotlin中的函数
  • private 为可见性修饰符
  • fun 为函数声明关键字
  • doSomeThing 为函数名
  • name:String 为函数参数,其中name为函数名,String为参数类型
  • 冒号后面的String为返回类型
private fun doSomeThing(name:String):String{
    println("我的名字是${name}")
    return "我的名字是${name}"
}

函数参数,可以为函数参数设置默认值 on 12/26/21 3:43 PM
private fun fix(name: String = "小强",age:Int = 1){
    println("我的名字是${name} ,年龄是 ${age}")
}
Unit函数,没有返回值的函数,上面的fix函数就是一个Unit函数 on 12/26/21 3:47 PM

匿名函数:定义时没有名字的函数,通常整体传递给其他函数或者从其他函数返回 函数也有类型,函数的类型由传入的参数和返回值类型决定 一下的nonameFunciton 是一个没有入参,返回值是一个String类型的函数,即nonameFunciton变量是一个函数类型

val nonameFunciton:()->String = {
    // 为什么不写return呢?匿名函数会自动将最后一行的结果最为函数的返回值 on 12/26/21 4:01 PM
    "this is a good day"
}
  • 和具名函数一样,匿名函数也可以有零个或者多个参数。
  • 当匿名函数中需要添加参数时,参数类型放在匿名函数的类型定义中,参数名防砸大括号即函数体重
  • 以下定义了一个入参是String,返回值是String 的匿名函数
val nonameFunctionWithParams:(String) -> String = {
    // 如果匿名函数只有一个参数,可以用it缺省替代 on 12/26/21 4:09 PM
    "${it} 说,今天是一个好天气"
}

以上的定义等价于下面的写法 on 12/26/21 4:05 PM

val nonameFunctionWithParams:(String) -> String = { name ->
    "${name} 说,今天是一个好天气"
}

定义匿名函数时的类型推断 ---> 返回值的类型推断 on 12/26/21 4:11 PM

val aFunction = {
    "将一个匿名函数赋值给了一个变量"
}

等价于 on 12/26/21 4:12 PM

val aFunction:() -> String  = {
    "将一个匿名函数赋值给了一个变量"
}

定义匿名函数时的类型推断 ---> 函数入参的类型推断 此时在方法体中,必须添加参数名以及参数类型

val a1Function = { name:String ->
    "这是一带参数的匿名函数类型推断"
}

等价于 on 12/26/21 4:17 PM

val a1Function: (String) -> String = {
    "这是一带参数的匿名函数类型推断"
}

lambda表达式,我们将匿名函数称为lambda表达式,匿名函数的定义称为lambda表达式,匿名函数的返回值称为lambda结果

  • 将匿名函数作为另一个函数的参数
  • 以下的方法含有两个参数,一个是name,类型为String,另一个是ageIS,类型为(age:Int) -> String ,是一个函数类型
fun withAFunction(name:String,ageIs:(age: Int) -> String){
    "${name} 是" + ageIs(0)
}
// 调用方式如下 on 12/26/21 4:30 PM
fun main1(){
    withAFunction("小明",{age:Int ->
        "${age} 岁"
    })

}
// 另一种调用方式 on 12/26/21 4:34 PM
val ageFunction:(Int)-> String = {
    "${it} 岁"
}
fun main2(){
    withAFunction("小明", ageFunction)
}

  • 简略写法:
  • 如果一个函数的lambda参数排在最后或者是唯一的参数,那么扩住lambda值参的一对圆括号可以省略
fun main3(){
    withAFunction("xiaoming"){
        "${it} 岁"
    }
}
函数的内联
  • lamnbda可以让我们灵活的编写应用,但是,也是有代价的。
  • 在虚拟机中,我们定义的lambda会以对象实例的形式存在,虚拟机会为所有同lambda打交道的变量分配内存,这样就产生了内存开销。
  • lambda内存开销会带来严重的性能问题,所有出现了一种叫内联的优化机制。
  • 内联会使虚拟机不再需要使用lambda对象实例,因而避免了变量内存分配,哪里需要使用lambda,编译器就会将函数体复制粘贴到哪里。
  • 也正是因为lambda的复制粘贴机制,所以使用lambda的递归函数无法使用内联,因为这样会导致复制粘贴无限循环。
函数引用,
  • 使用双引号,将一个具名函数作为另一个函数的参数
fun aNewFunction(name: String,ageFunc:(Int) -> String){
    "${name} is ${ageFunc(1)}"
}


fun ageFunction(age: Int):String{
    return "${age} 岁"
}
// 使用 on 12/26/21 5:24 PM
fun main4(){
    aNewFunction("xiaoli",::ageFunction)
}
  • 函数类型做为一个返回类型
  • 函数类型也是一个有效地返回类型,也即是说可以定义一个能返回函数的函数
fun anotherFunction():(String) -> String{
    val age = 18

    // 此处返回了一个匿名函数作为返回值 on 12/26/21 5:31 PM
    return { name:String ->
        "我的名字是${nonameFunciton},我今年${age}岁"
    }
}
// 使用 on 12/26/21 5:29 PM
fun main5(){
    // 定义一个变量,接收函数的返回值,也就是将匿名函数赋值给了一个变量 on 12/26/21 5:30 PM
    val func = anotherFunction()
    // 使用该变量执行匿名函数 on 12/26/21 5:30 PM
    println(func("小李"))
}
  • 上一个例子也引出了闭包的概念

  • 匿名函数能够修改并引用定义在自己的作用域之外的变量,匿名函数引用着定义自身的函数里的变量,Kotlin中的lambda就是闭包

  • 在上面①处的例子中,返回的匿名函数可以访问变量age,也就是说可以引用anotherFunction函数中定义的变量。

  • 所以此处形成了一个闭包。age变量和匿名函数都在anotherFunction函数中定义。

能接收函数或者返回函数的函数被称为高阶函数,高阶函数广泛应用于函数式编程中。