KotlinKotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KotlinKotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )相关的知识,希望对你有一定的参考价值。

文章目录





一、函数头声明



函数头声明 格式如下 :

可见性修饰符 函数声明关键字 函数名 (函数参数) : 函数返回值类型 

函数头示例 :

private fun hello(name:String, age:Int):String
  • 可见性修饰符 : private
  • 函数声明关键字 : fun
  • 函数名 : hello
  • 函数参数 : name:String, age:Int
  • 函数返回值类型 : String

代码示例 :

fun main() 
    val name = "Tom"
    val age = 18

    println(hello(name, age))


private fun hello(name: String, age: Int): String 
    return "Name is $name, age $age, type is $if(name == "Tom") "Cat" else "Mouse""

执行结果 :

Name is Tom, age 18, type is Cat





二、函数参数



1、默认参数值


默认参数值 : Kotlin 语言 中的 函数参数 , 可以 在定义时 指定参数默认值 ;

代码示例 :

fun main() 
    val name = "Tom"
    val age = 18

    println(hello())


private fun hello(name: String = "Tom", age: Int = 18): String 
    return "Name is $name, age $age, type is $if(name == "Tom") "Cat" else "Mouse""

执行结果 :

Name is Tom, age 18, type is Cat


2、具名参数


具名参数 : Kotlin 中的 函数参数顺序 必须按照定义的顺序传递 ,

如果使用 具名参数 , 可以不必按照参数顺序传递参数 ;


代码示例 : 在下面的代码中 , 函数参数为 name: String, age: Int ,

先传递 String 类型值 , 然后再传递 Int 类型值 ,

但是使用具名参数 后 , 可以 先传递 Int 参数 , 再传递 String 参数 ;

fun main() 
    println(hello(age = 18, name = "Tom"))


private fun hello(name: String, age: Int): String 
    return "Name is $name, age $age, type is $if(name == "Tom") "Cat" else "Mouse""

执行结果 :

Name is Tom, age 18, type is Cat





三、Unit 函数



Java 语言没有返回值的函数返回类型void ;

Kotlin 语言 没有返回值的函数 其返回类型是 Unit , 该函数又称为 Unit 函数 ;


Kotlin 语言中 推出 Unit 类型概念 , 是为了 兼容 泛型 概念 ,

如果 函数没有返回值 , 就可以 忽略该类型 , 返回 void ,

但是在 泛型 概念中 , 必须有一个确定的 类型 , 因此这里引入 Unit 类型 ;


代码示例 : 在下面代码的 hello() 函数的返回值类型是 Unit 类型 , 如果打印该返回值 , 打印结果为 kotlin.Unit ;

fun main() 
    println(hello())


fun hello(): Unit 
    println("Hello")

执行结果 :

Hello
kotlin.Unit





四、TODO 函数抛出异常返回 Nothing 类型



在 Kotlin 中 有一种函数 TODO 函数 ,

TODO 函数 唯一的作用 就是 抛出异常 ,

该函数 执行永远失败 , 并且 返回 Nothing 类型 ;


TODO 函数原型如下 :

/**
 * 总是抛出[NotImplementedError],表示操作未实现。
 *
 * @param reason一个解释为什么缺少实现的字符串。
 */
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = 
	throw NotImplementedError("An operation is not implemented: $reason")

代码示例 :

fun main() 
    TODO("TODO 抛出异常")

执行结果 :

Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: TODO 抛出异常
	at HelloKt.main(Hello.kt:2)
	at HelloKt.main(Hello.kt)





五、反引号函数名



Java 的函数名要求 :

  • 只能由 字母数字下划线 构成
  • 首字母并不能是数字
  • 不能是关键字

Kotlin 中 函数名 可以使用 空格 , 特殊字符 , 关键字 , 前提是 该函数名 必须使用 反引号 ;

注意 Kotlin 和 Java 中的关键字不同 , 不管是哪个语言的关键字 , 都不能作为函数名 , 但是如果将关键字 使用反引号 括起来 , 就可以使用其作为函数名 ;


代码示例 :

fun main() 
    `~!@# Hello World %^&*`()


fun `~!@# Hello World %^&*`()
    println("Hello World")

执行结果 :





六、匿名函数



声明函数时 , 没有 函数名 的函数 是 匿名函数 ;

匿名函数 可以作为 函数参数 , 也可以作为 函数返回值 ;


匿名函数 可以 定制修改 已有的 函数 , 如 : 标准库中的函数 ;


Kotlin 中对 CharSequence 类进行了扩展 , 定义了 count(predicate: (Char) -> Boolean) 扩展函数 , 接收一个 (Char) -> Boolean 类型的函数 , 用于 返回匹配给定 匿名函数 的字符数 ;

/**
 * 返回匹配给定[谓词 predicate 匿名函数]的字符数。
 */
public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int 
    var count = 0
    for (element in this) if (predicate(element)) ++count
    return count


代码示例 : 在下面的代码中 , 传入了 匿名函数

 letter->
	letter == 'l'

作为参数 , 其作用是 返回 "Hello" 字符串中 , 字符符合 letter == 'l' 要求的字符个数 ;

fun main() 
    // 统计字符串长度
    val toalCount = "Hello".count()
    println("toalCount = $toalCount")

    // 统计字符串中 l 字符长度
    val toalLCount = "Hello".count( letter->
        letter == 'l'
    )
    println("toalLCount = $toalLCount")

执行结果 :

toalCount = 5
toalLCount = 2





七、匿名函数的函数类型



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

可以作为 函数参数 传递给函数 ,

因此 , 匿名函数 与 变量 一样 , 也存在 对应的 函数类型 ;


函数类型 由 参数返回值 决定 ;

有 相同 参数顺序 , 参数个数返回值类型 的函数 , 其 函数类型相同 ;


如上个章节 , 扩展函数 CharSequence.count 接收的匿名函数参数 predicate , 其函数类型是 (Char) -> Boolean ;

public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int

代码示例 : 声明一个函数类型变量 , 然后为其赋值 , 最后执行上述函数 ;

fun main() 
    // 声明 函数类型 变量
    val helloFun: ()->String

    // 为 函数类型变量 赋值一个 匿名函数
    helloFun = 
        "Hello World"
    

    // 执行 函数类型 变量对应的 函数
    val str = helloFun()
    println(str)

执行结果 :

Hello World





八、匿名函数的隐式返回



普通函数 返回值 , 都是 显示返回 , 如 : 使用 return 关键字 , 返回返回值 ;

匿名函数 的 返回值 不需要使用 return 关键字 ,

匿名函数 可以 隐式 返回 函数体最后一行语句 ;


代码示例 : 在匿名函数中 , 第一行是 Int 值 , 第二行是 Boolean 值 , 第三行是 String 值 , 最后返回的是最后一行 String 值 ;

fun main() 
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: ()->String = 
        123
        true
        "Hello World"
    

    // 执行 函数类型 变量对应的 函数
    println(helloFun())

执行结果 :








九、匿名函数参数



匿名函数 可以不带参数 , 也可以带多个参数 ;


不带参数的匿名函数 :

    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: ()->String = 
        "Hello World"
    

带参数的匿名函数 : 匿名函数参数类型 放在 函数类型 定义中 , 参数名 放在 函数体 内 ;

    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String =  age ->
        "Hello World $age"
    

上面的 匿名参数 , 函数类型 (Int)->String ,

函数类型 中 , 只有参数类型 , 没有参数名 ,

函数体中 age ->age 就是对应的 Int 类型参数的 参数名 ,

函数体中 , 只有参数名 , 没有参数类型 ;


代码示例 :

fun main() 
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String =  age ->
        "Hello World $age"
    

    // 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数
    println(helloFun(18))

执行结果 :





十、匿名函数 it 关键字



如果 匿名函数 只有 1 个函数参数 , 在 匿名函数 的 函数体 中 , 可以 省略 函数名 声明 , 使用 it 关键字 代替 ;


代码示例 : 在下面的 匿名函数中 , 只有 一个 Int 类型的函数参数 , 在函数体中可以省略 age -> 参数名 声明 , 可以 使用默认的 it 关键字 作为 参数名 ;

fun main() 
    // 声明 函数类型 变量, 并为其赋值 匿名函数
    val helloFun: (Int)->String = 
        "Hello World $it"
    

    // 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数
    println(helloFun(18))

执行结果 :

Hello World 18









十一、匿名函数变量类型推断



定义变量 时 , 如果将变量值 直接赋值给该变量 , 那么就可以 不用显示声明该变量的类型 ;

下面的代码中 , 定义 name 变量 , 为其 赋值 “Tom” 字符串 String 类型变量值 , 则 该变量被自动推断为 String 类型变量 ;

var name = "Tom"

如果 变量没有赋值 , 则声明变量时 , 必须显示声明该变量的类型 ;

var name: String

如果定义一个 函数类型 变量 , 将 匿名函数 作为变量值 赋值给该变量 , 此时可以 不需要显示声明 函数类型 变量的值 ;

下面的代码中的 函数类型 : ()->String 可以省略 , 由 类型推断 来确定 helloFun 只读变量的值 ;

    val helloFun: ()->String = 
        val name = "Tom"
        "Hello World, $name"
    

代码示例 : 如下代码中 , helloFun 变量没有设置变量类型 , 其类型由 赋值给 该变量的 匿名函数类型自动推断得来 , 匿名函数类型为 ()->String 类型 ;

fun main() 
    val helloFun = 
        val name = "Tom"
        "Hello World, $name"
    
    println(helloFun())





十二、匿名函数参数类型自动推断



如果 需要 使用 自动类型推断 确定 匿名函数参数类型 ,

则在 匿名函数 的 函数体中 , 必须 显示声明 匿名函数变量名变量类型 ;

匿名函数 返回值 类型 , 是根据 匿名函数 函数体最后一行表达式的值 进行自动推断的 ;


代码示例 : 在下面的函数中 , 匿名函数的函数体中 , 使用 变量名: 变量类型 -> , name: String, age: Int -> , 显示声明了匿名函数的 参数类型 , 这样就可以使用 类型推断 , 自动推断出 匿名函数 的参数类型 ;

该匿名函数 函数体 最后一行表达式 的 类型String 类型 , 其 返回值类型就是 String 类型 ;

fun main() 
    val helloFun =  name: String, age: Int ->
        "Hello World, $name, $age"
    
    println(helloFun("Tom", 18))

执行结果 :

Hello World, Tom, 18

如果 不使用 匿名函数 类型推断 ,

则在 函数变量 声明时 , 确定 函数参数 类型 ,

匿名函数 函数体 中 , 确定 函数参数名 即可 ,

示例代码如下 :

fun main() 
    val helloFun: (String, Int)->String =  name, age ->
        "Hello World, $name, $age"
    
    println(helloFun("Tom", 18))

执行结果 :

Hello World, Tom, 18




十三、Lambda 表达式



匿名函数 又称为 Lambda 表达式 , 匿名函数的 返回值 是 Lambda 结果 ;





十四、 函数参数为 Lambda 表达式



定义函数 时 , 函数的参数 可以是 函数类型的变量 ,

可以传递一个 匿名函数 作为 函数参数 ;

匿名函数 就是 Lambda 表达式 ;


代码示例 : 在下面的代码中 ,

  • 函数参数 :

studentDoSomething 函数的 第三个参数为 action: (String, Int) -> String ,

其参数类型为 (String, Int) -> String , 是一个 函数类型 ;

  • 函数类型变量 :

main 函数中 , 定义函数类型变量 actionFun , 之后 该变量会作为函数参数传递给函数 ,

同时使用了 匿名函数 , 为该函数类型变量 actionFun 赋值 ;

  • 匿名函数类型自动推断 :

在该 匿名函数中 , 使用了 自动类型推断 , 在函数体中的参数列表 ,

声明了 完整的 参数名:参数类型 , name: String, age: Int -> ;

  • 函数变量作函数参数 :

在最后 , 将 函数类型 变量 actionFun 传递给了 studentDoSomething 函数 , 作为其第三个参数使用 ;

fun main() 
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun =  name: String, age: Int ->
        "student $name $age years old, say hello"
    

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);


fun studentDoSomething(name: String, age: Int,
                       action: (String, Int) -> String) 
    val act = action(name, age);
    println(act)

执行结果 :

student Tom 18 years old, say hello





十五、Lambda 表达式作为参数的简略写法



1、Lambda 表达式作为唯一参数的简略写法


如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 唯一参数 , 那么 Lambda 表达式外面的圆括号可以省略 ;


代码示例 :

fun main() 
    // 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数
    doSomething(
        "Hello World"
    )


fun doSomething(action: () -> String) 
    val act = action();
    println(act)

此时将鼠标移动到 Lambda 表达式 上 , 也就是匿名函数中 , 会提示

Lambda argument should be moved out of parentheses 
参数应该移出圆括号

Kotlin 建议我们移除 Lambda 表达式 外面的圆括号 ;


修改后的代码示例 :

fun main() 
    // 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数
    doSomething 
        "Hello World"
    


fun doSomething(action: () -> String) 
    val act = action();
    println(act)

执行结果 :


2、Lambda 表达式作为最后一个参数的简略写法


如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 若干参数的最后一个参数 , 那么 Lambda 表达式可以提到括号外面 ;


在上一个章节的如下代码 , 可以直接 将 匿名函数 作为函数参数进行传递 , 不必使用 函数类型 变量名作为参数 ,

fun main() 
    // 定义函数类型变量, 之后作为函数参数传递给函数
    val actionFun =  name: String, age: Int ->
        "student $name $age years old, say hello"
    

    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18, actionFun);

直接使用匿名函数作为函数参数 的效果 :

fun main() 
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18,  name: String, age: Int ->
        "student $name $age years old, say hello"
    )

匿名函数 , 也就是 Lambda 表达式 作为最后一个参数 , 可以提取到括号外面 , 代码效果如下 :

fun main() 
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18)  name: String, age: Int ->
        "student $name $age years old, say hello"
    


最终的简化后的代码示例 :

fun main() 
    // 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作
    studentDoSomething("Tom", 18)  name: String, age

以上是关于KotlinKotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )的主要内容,如果未能解决你的问题,请参考以下文章

KotlinKotlin函数那么多,你会几个?

KotlinKotlin 与 Java 互操作 ① ( 变量可空性 | Kotlin 类型映射 | Kotlin 访问私有属性 | Java 调用 Kotlin 函数 )

KotlinKotlin 与 Java 互操作 ② ( @JvmField 注解字段给 Java | @JvmOverloads 注解修饰函数 | @JvmStatic 注解声明静态成员 )

KotlinKotlin 与 Java 互操作 ③ ( Kotlin 中处理 Java 异常 | Java 中处理 Kotlin 异常 | @Throws 注解处理异常 | 函数类型互相操作 )

KotlinKotlin 中使用 Lambda 表达式替代对象表达式原理分析 ( 尾随 Lambda - Trailing Lambda 语法 | 接口对象表达式 = 接口#函数类型对象 )

KotlinKotlin 中使用 Lambda 表达式替代对象表达式原理分析 ( 尾随 Lambda - Trailing Lambda 语法 | 接口对象表达式 = 接口#函数类型对象 )