Kotlin学习系列——函数,Lambda表达式

柔光的暖阳◎ 2023-06-28 09:50 38阅读 0赞

函数

Kotlin中,使用fun关键字定义一个函数,函数参数使用 Pascal 表示法定义,即 name: type。参数用逗号隔开。每个参数必须有显式类型。

  1. fun foo(arg1:String,arg2:Boolean,arg3:Int):Int{
  2. //do somethong
  3. return 1
  4. }
  5. 1
  6. 2
  7. 3
  8. 4

当函数只有单个表达式时,可以省略花括号,直接写在=之后,如果返回值类型可由编译器推断出来,也可以省略返回值类型:

  1. fun foo(arg1:String,arg2:Boolean,arg3:Int)=1//省略花括号和返回值类型后的函数形式
  2. 1

具有块代码体的函数,必须显式指定返回值类型,除非返回值类型是Unit。编译器不会推断具有块代码体的函数的返回类型,因为这样的函数在代码体中可能有复杂的控制流,并且返回类型对于读者(又是甚至是编译器)是很不明显的。

  1. fun foo(arg1:String,arg2:Boolean,arg3:Int):Int{
  2. print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3)
  3. return 0
  4. }
  5. 1
  6. 2
  7. 3
  8. 4

调用函数使用传统的方法

  1. var value=foo()
  2. 1

成员函数的调用要使用点表示法

  1. R().foo()
  2. 1

函数还可以使用中缀表示法来调用,当满足一下条件时:

  1. infix fun Int.add(x:Int):Int{
  2. return this+x
  3. }
  4. fun printAdd(){
  5. val value=2 add 3//中缀表示法调用add函数
  6. print("the value is $value")
  7. }
  8. 1
  9. 2
  10. 3
  11. 4
  12. 5
  13. 6
  14. 7
  15. 8

我们还可以在调用函数的时候使用命名的参数:

  1. fun foo(arg1:String="Hello Kotlin",arg2:Boolean,arg3:Int){
  2. print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3)
  3. }
  4. fun printFoo(){
  5. foo(arg2 = true,arg3 = 7)//命名参数调用foo函数
  6. }
  7. 1
  8. 2
  9. 3
  10. 4
  11. 5
  12. 6
  13. 7

函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量。

  1. fun foo3(arg1: String,arg2: Boolean=false,arg3: Int=0){
  2. //默认参数要放到函数参数的最后面
  3. print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3)
  4. }
  5. fun printFoo3(){
  6. foo3("Hello Kotlin")//调用时,可以不传入默认参数的值,这时默认参数就会使用默认值
  7. }
  8. 1
  9. 2
  10. 3
  11. 4
  12. 5
  13. 6
  14. 7

我们还可以为函数定义可变数量的参数,只要用vararg关键字修饰即可:

  1. //可变数量的参数
  2. fun foo4(vararg args:Int){
  3. for (arg in args){
  4. print(arg.toString()+",")
  5. }
  6. }
  7. fun printFoo4(){
  8. foo4(1,2,3,4,5)//1,2,3,4,5,
  9. }
  10. 1
  11. 2
  12. 3
  13. 4
  14. 5
  15. 6
  16. 7
  17. 8
  18. 9
  19. 10

如果我们已经有一个数组并希望将其内容传给该函数,我们使用伸展(spread)操作符(在数组前面加 *):

  1. //可变数量的参数
  2. fun foo4(vararg args:Int){
  3. for (arg in args){
  4. print(arg.toString()+",")
  5. }
  6. }
  7. fun printFoo4(){
  8. val values= intArrayOf(1,2,3,4,5)
  9. foo4(*values)//使用扩展运算符传一个数组给可变参数 }
  10. 1
  11. 2
  12. 3
  13. 4
  14. 5
  15. 6
  16. 7
  17. 8
  18. 9
  19. 10
  20. 11

高阶函数

高阶函数可以将一个函数作为参数或返回值:

  1. fun add2(x:Int=0,y:Int=0):Int{
  2. return x+y
  3. }
  4. fun operate(x:Int=0,y:Int=0,body:(Int,Int)->Int){//body是一个函数类型,传入两个Int类型参数,返回一个Int类型参数 print("this result is "+body(x,y)) } fun getValue(){ operate(3,7,::add2) }
  5. 1
  6. 2
  7. 3
  8. 4
  9. 5
  10. 6
  11. 7
  12. 8
  13. 9
  14. 10
  15. 11

Lambda表达式

以上的operate()方法,我们还有一个更简洁的调用方式,即传入一个lambda表达式:

  1. fun getValue(){
  2. operate(3,7,::add2)//函数参数传入一个函数
  3. operate(3,7,{x,y->x+y})//函数参数传入一个lambda表达式
  4. }
  5. 1
  6. 2
  7. 3
  8. 4

Lambda 表达式的完整语法形式,即函数类型的字面值如下:

  1. val sum = { x: Int, y: Int -> x + y }
  2. 1

lambda 表达式总是被大括号括着, 完整语法形式的参数声明放在括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后。如果推断出的该 lambda 的返回类型不是 Unit,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。

如果我们把所有可选标注都留下,看起来如下:

  1. val sum: (Int, Int) -> Int = { x, y -> x + y }
  2. 1

当函数参数是最后函数的最后一个参数,并且你传入一个lambda表达式作为相应的参数,则可以在圆括号之外指定它:

  1. fun getValue(){
  2. operate(3,7,::add2)//函数参数传入一个函数
  3. operate(3,7,{x,y->x+y})//函数参数传入一个lambda表达式
  4. operate(3,7){
  5. //函数参数作为函数的最后一个参数,并且传入的是一个lambda表达式,可以在在圆括号外指定
  6. x,y->x+y
  7. }
  8. }
  9. 1
  10. 2
  11. 3
  12. 4
  13. 5
  14. 6
  15. 7

如果lambda表达式只有一个参数,kotlin可以自己计算出签名,它允许我们不声明唯一的参数,并且隐含的为我们声明其名称为it:

  1. fun upCase(str:String,body:(String)->String):String{//body是一个函数参数,传入一个String类型参数,返回一个String类型 return body(str) } fun transform(){ upCase("HelloKotlin"){//函数字面值只有一个参数,可以省略参数声明,其名称是it it.toUpperCase() } }
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8

如果lambda表达式是调用的唯一参数,则调用中的圆括号可以完全省略:

  1. fun String.upper(body:(String)->String):String{ return body(this) } fun transform(){ "HelloKotlin".upper { it.toUpperCase() }//lambda是调用的唯一参数,则调用的圆括号可以省略 }
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7

匿名函数

匿名函数与常规函数一样,只是省略了函数名称而已:

  1. fun String.upper(body:(String)->String):String{ return body(this) } fun transform(){ "HelloKotlin".upper { it.toUpperCase() }//lambda表达式是调用的唯一参数,则调用的圆括号可以省略 "HelloKotlin".upper(fun(str:String):String{ //将匿名函数作为一个函数参数传入 return str.toUpperCase() }) }
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10

Lambda表达式和匿名函数之间的另一个区别是非局部返回的行为。一个不带标签的 return 语句总是在用 fun 关键字声明的函数中返回。这意味着 lambda 表达式中的 return 将从包含它的函数返回,而匿名函数中的 return 将从匿名函数自身返回。

  1. fun foo() {
  2. ints.forEach {
  3. if (it == 0) return//这个 return 表达式从最直接包围它的函数即 foo 中返回。
  4. print(it)
  5. }
  6. }
  7. 1
  8. 2
  9. 3
  10. 4
  11. 5
  12. 6

值得注意的是:这种非局部的返回只支持传给内联函数的 lambda 表达式。 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。

  1. fun transform():String{
  2. "HelloKotlin".upper {
  3. print(it.toUpperCase())
  4. return@upper it.toUpperCase()//返回必须加标签限制
  5. }
  6. "HelloKotlin".upper(fun(str:String):String{
  7. return str.toUpperCase()//从匿名函数返回
  8. })
  9. }
  10. 1
  11. 2
  12. 3
  13. 4
  14. 5
  15. 6
  16. 7
  17. 8
  18. 9

带接收者的函数字面值

Kotlin提供了指定的接收者调用函数字面值的功能。在函数字面值的函数体中,可以调用该接收者对象上的方法而无需任何额外的限定符。
这样的函数字面值的类型是一个带有接收者的函数类型:

  1. sum : Int.(other: Int) -> Int
  2. 1

该函数字面值可以这样调用,就像它是接收者对象上的一个方法一样:

  1. 1.sum(2)
  2. 1

匿名函数语法允许你直接指定函数字面值的接收者类型。 如果你需要使用带接收者的函数类型声明一个变量,并在之后使用它,这将非常有用。

  1. val sum = fun Int.(other: Int): Int = this + other
  2. 1

发表评论

表情:
评论列表 (有 0 条评论,38人围观)

还没有评论,来说两句吧...

相关阅读