- 高阶函数:kotlin提供的一种特殊的函数,可以将函数作为参数传入或者将函数作为返回值返回。
-
kotlin系统函数中提供了一系列的高阶函数,比如kotlin中的集合的操作函数,kotlin中的高阶函数:let,with,apply,use几个函数(当前文章着重学习高阶函数和lambda表达式,具体的高阶函数后续新开文章学习总结),即:
-
- 函数类型(Function Type)
- 结合高阶函数,将函数作为参数或者作为返回类型,为了支持这种实现kotlin提供了函数类型这一概念并声明了函数类型的语法,整理如下:
- 语法:(参数类型列表)-> 返回值类型:注意基础的函数类型的是参数类型列表,实例:(Int,Int)-> Int : 接受两个Int的参数,返回一个Int的返回值。
- 参数的个数可以为null,即此时的语法() -> Int:没有参数返回一个Int的值,也可以没有返回值,即()-> Unit :此时需要注意的是Unit不能省略。
- A.(B) -> C:函数类型提供一个接受者:即:表示一个可以对类型为
A
的接受者调用的函数, 参数类型为B
, 返回值类型为C
. 对这种函数类型, 我们经常使用 带接受者的函数字面值。
- 挂起函数(Suspending function)是一种特殊类型的函数, 它的声明带有一个特殊的 suspend 修饰符, 比如:
suspend () -> Unit
, 或者:suspend A.(B) -> C
. -
带有参数名的参数列表:参数列表中可以只是参数类型 , 也可以加上参数的变量名称 , 参数名称可以用于说明参数的含义 , 增加函数类型的理解性
-
函数类型可空:函数类型 与 普通变量类型一样 , 也分为可空类型 , 非空类型 两类 。默认的函数类型都是非空类型 , 即函数定义时 , 需要对其进行初始化 , 或延迟初始化 。可空的函数类型声明时 , 需要在函数类型外部加上括号 , 并在右侧添加 可空符号 :
( ( 参数列表 ) -> 返回值类型 )
-
函数类型也可以使用括号组合在一起: (Int) -> ((Int) -> Unit)
- 可以给函数类型指定别名:typealias ClickHandler = (Button, ClickEvent) -> Unit
- 函数类型的实例化:lambda表达式,匿名函数,已声明的函数,函数变量,函数类型派生类。
- lambda表达式:val add1:(Int,Int) -> Int = {a:Int,b:Int -> a+b}:语法上需要注意的是:函数类型的语法:()-> Int lambda表达式的语法:{a:Int,b:Int -> a+b},整体的理解: 变量关键字 变量名:()-> 返回值类型,Int = {a:Int,b:Int -> a+b} 整个是返回值类型。
- 匿名函数:var add2 : (Int, Int) -> Int = fun (a : Int, b : Int) : Int {return a + b}
-
已声明的函数:需要注意的是::的使用方式,顶层函数可以直接使用:: 类中的函数使用类::函数来实例化函数类型后给变量。
-
函数变量:已经声明的函数类型属性 , 可以是顶层属性 , 成员属性 , 扩展属性 即:
-
函数类型的派生类:我去直接拿一个类实现一个函数类型,在其重写函数invoke中返回数值类型,函数类型可以看做一个接口 , 类可以实现该接口 , 在实现类中实现具体的函数操作 , 该 函数类型接口的实现类 , 可以赋值给函数类型变量
- 针对上面这几项可以参考下面的参考文章,参考文章说明的更详细包括事例更有代表性。
-
函数类型的自动推断:像kotlin的参数似的,kotlin可以根据第一次赋值推断出参数的类型,所以可以不直接指定参数的类型,函数类型的返回值类型,kotlin也可以通过lambda或者匿名函数或者其他几种实例化方式推断出返回值的类型,还有函数类型的参数类型也可以通过lambda几种实例化进而推断出类型,所以二者都可以省略,需要注意的是kotlin推断不出其对应的类型的时候 参数类型和返回值类型都不能省略。比如Unit不能够省略。
- 带有接受者的函数类型和不带有接受者的函数类型转换:
-
转换方式:前者转换为后者方式:将接受者对象作为第一个参数传递给后者,即后者在前者参数的基础上多一个参数,此参数为接受者对象。
-
- 函数类型的调用:
- 函数类型的实现函数是invoke函数,所以通过变量名的invoke调用即可以调用声明的这个函数类型。即变量名.invoke(参数)
- 直接调用 : 也可以通过 函数类型变量名(参数列表) 直接调用该函数 , 将该变量名称当做函数名称来使用,即 变量名(参数)
- 带有接受者的函数类型和不带有接受者的函数类型转换:
-
- 语法:(参数类型列表)-> 返回值类型:注意基础的函数类型的是参数类型列表,实例:(Int,Int)-> Int : 接受两个Int的参数,返回一个Int的返回值。
- 结合高阶函数,将函数作为参数或者作为返回类型,为了支持这种实现kotlin提供了函数类型这一概念并声明了函数类型的语法,整理如下:
- lambda表达式:
- lambda表达式的语法:{ x : int ,y:Int -> x+y }
- kotlin中lambda表达式始终用花括号包围,注意参数部分没有用括号括起来,箭头可以把实参列表和lambda的函数体隔开
- 其中 箭头 -> 前的部分是参数,箭头后面是函数体
- 参数 -> 函数体:多个参数中间使用,隔断,参数的类型若是kotlin能够自动推断出来可以不直接指明其类型。
- lambda表达式的函数体可以是单行也可以是多行,最后一行默认是lambda表达式的返回值需要注意的是最后一行返回值的类型必须和lambda表达式变量声明的返回值的值类型保持一致,若不一致则会出现异常。
- 由lambda表达式的语法可知:lambda表达式是不能够直接声明返回值的类型的,通常是由其函数体部分的最后一句语句推断出来的。
-
lambda表达式类型(变量)完整语法:val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
备注:
- 常量关键字 常量名称 : 参数 -> 返回类型 = { lambda表达式 }
- 参数声明在大括号中,多个参数使用,隔开,0参数则()表示,单参数使用(参数)表示,参数中的某个参数也可以是一个参数类型(lambda表达式或者匿名函数等实例化即可) 参数可选即kotlin若能通过lambda表达式推断出参数的类型则语法中的参数类型(大括号)可以省略。
- lambda若能够自动推断出返回值的类型则返回类型(变量声明的返回值类型)可以省略,但是Unit不能省略,因为kotlin是默认函数体的最后一行是返回值所以倘若不需要返回值则必须声明Unit(此处的声明是函数类型的语法声明所以Unit不能去掉)
-
由于参数类型中的参数类型和返回值类型可以省略所以此时省略后的语法:val sum = { x: Int, y: Int -> x + y }
-
针对一个高阶函数,倘若高阶函数的最后一个参数是lambda表达式,那么此函数调用的时候可以将最后一个参数(lambda表达式)放到函数参数大括号的外面,即:
-
倘若lambda表达式仅仅有一个参数,即其参数唯一的时候此时lambda表达式可以不声明参数即可以忽略->前面的语法,直接去写表达式的函数体,此时唯一参数可以使用关键字it标识,即:
-
常规lambda表达式是没有return语句的,最后一句语句就是返回值,返回值类型就是lambda表达式的返回值类型,如果使用 带标签限定的 return 语法, 你可以在 Lambda 表达式内明确地返回一个结果值(带标签限定的return语法:和标签限定this一致,通过标签标明return指向的那个对象,即return@filter表示 该返回值是函数filter的返回值). 否则, 会隐含地返回 Lambda 表达式内最后一条表达式的值.
-
在高阶函数调用的时候,需传入的lambda表达式,如果 Lambda 表达式的某个参数未被使用, 你可以用下划线来代替参数名:
- Lambda表达式的解构:此处不详细描述,放到对应的解构模块去解析。
-
- lambda表达式的语法:{ x : int ,y:Int -> x+y }
- 匿名函数:kotlin中函数的声明语法中不声明对应的函数名则被称为匿名函数:
- 语法:fun(参数) :返回值 { 函数体}
- 匿名函数和lambda表达式的区别:
- lambda表达式语法:{参数-> 函数体},由其语法可了解到 lambda表达式是没有返回值返回的,通常根据其函数体的最后一句语句判断出返回值类型,若想直接声明返回值的类型则可以通过匿名函数来声明。
- lambda表达式是没有return语句的,所以lambda表达式是不能够直接返回的,若返回需要从lambda表达式所属的函数中返回,但是匿名函数则不然,匿名函数则存在return语句可以在自己的函数体执行完毕后直接返回。
-
和函数相比,匿名函数的区别是省略了函数名称,其函数体可以是单行也可以是多行,若单行可以省略掉{},此时成为匿名函数表达式(使用=替换掉原有的{}),即:
- 匿名函数的参数类型和返回值类型若能够自动推断出来则可以省略掉其类型声明,若函数体是多行且没有返回值的时候则必须声明Unit,和java一致,没有返回值则必须声明void一致。
- 匿名函数作为高阶函数的最后一个参数的时候,在调用的时候匿名函数不能放到高阶函数的大括号外部(参数),能够放到其外部的仅有lambda表达式。
- 闭包:
- 闭包:在java中线程或者监听方法中,若要是想访问外部函数中声明的局部变量则必须将其定义为final,且在其内部不能够修改局部变量(final的局部变量不被修改),对于此情况 kotlin提供了闭包的概念,在闭包中可以直接访问外部函数中的局部属性,对于var声明的外部属性还可以修改属性。
-
闭包的原理:针对闭包由其编译后的JAVA文件可知:将其外部属性编译成为一个final的局部对象变量(比如局部变量是int 编译后变量名对应的是Integer对象)且这个局部对象变量还声明了一个属性其属性对应的值是先前局部变量的值,即int i = 5编译后是:Integer i = new Integer(),i.element = 5所以在闭包中不仅可以访问这个属性还可以修改这个属性:
-
lambda表达式,匿名函数的函数体都可以看做其闭包,即在其函数体内都可以访问外部函数声明的局部属性甚至可以直接修改其外部属性。
参考文章:
kotlin:函数类型
kotlin:常用的高阶函数
kotlin:list的常见操作函数
kotlin:lambda表达式
kotlin:lambda表达式
kotlin:闭包原理
kotlin:闭包