Kotlin基础四:高阶函数和Lambda

前言

lambda表达式在java和kotlin中都有运用。方便开发者扩展一些自己的工具集,以及增加自己的开发效率。

函数类型

kotlin 复制代码
(参数类型,参数类型...) -> 返回值类型

函数默认返回值类型Unit,声明一个函数类型时,如果函数类型没有确切的返回值类型需要显示的指明为Unit

kotlin 复制代码
// 无参数,没有确切返回值类型,返回Unit
() -> Unit
// 有两个参数,返回String
(Int,String) -> String

声明一个函数类型为可空时?,需要将函数类型用()括起来,在()后面加?

kotlin 复制代码
// 函数类型可空
(() -> Unit)?
// 函数类型可空
((Int,String) -> String)?
kotlin 复制代码
// 延迟初始化,无需初始化值
lateinit var block:()->Unit
// 可空函数类型
var block1:(()->Unit)?=null
// 普通函数类型初始化
var block2:(()->Unit)={}

属性类型和函数类型的声明语法上是一样的。

Lambda表达式

kotlin 复制代码
{参数名:参数类型,参数名:参数类型 -> 函数体}

Lambda表达式用花括号{}括起来的一段代码。->左边是Lambda表达式的参数列表,->右边是函数体。Lambda最后一行代码是这个Lambda的返回值。和if when 表达式最后一行代码返回值类似。 用Lambda表达式完成对函数类型的初始化:

kotlin 复制代码
val block:(Int,String) ->String = {a:Int,b:String -> "$a $b"}

用=来连接函数类型和Lambda表达式。 Kotlin编译器可以推断出该函数类型,我们可以在声明中省略它:

kotlin 复制代码
val block={a:Int,b:String -> "$a $b"}

Lambda语法定义格式:

kotlin 复制代码
{参数名:参数类型,参数名:参数类型 -> 函数体}

当函数类型有且只有一个参数类型时,初始化这个函数,可以在Lambda表达式的花括号中省略单个参数的声明和->。该参数将被隐式的声明为it。

kotlin 复制代码
val block:(String) -> Unit = { println(it) }

也可以不使用隐式名称it,按照语法定义编写一个Lambda表达式

kotlin 复制代码
val block:(String) ->Unit = {name:String -> println(name) }

函数类型实例调用

kotlin 复制代码
val block:(String) -> Unit = { println(it) }
fun test() {
    block("kotlin")
}

函数类型实例的调用和普通函数调用没有什么区别,在函数名后跟小括号()。在()中传入在声明时的参数即可。 可以使用funName.invoke()调用一个函数类型的实例。invoke方法是调用操作符()的重载。 kotlin.jvm.functions.Functions.kt文件中 Function2中:

kotlin 复制代码
public interface Function2<in P1, in P2, out R> : kotlin.Function<R> {
    public abstract operator fun invoke(p1: P1, p2: P2): R
}

调用一个函数类型的实例有两种方式:

kotlin 复制代码
block("kotlin")和block.invoke("kotlin") 都可以完成对函数类型实例block的调用

高阶函数

高阶函数:将函数用作参数或返回值的函数。一个函数拥有函数类型的参数,或它的返回值类型是函数类型。

kotlin 复制代码
fun test() {
    val left = 1
    val right = 2
    val block = {left:Int,right:Int -> left+right}
    val result = sum(left,right,block)
    println(result)
}
fun sum(left:Int,right:Int,block:(Int,Int)->Int):Int{
    return block.invoke(left,right)
}

通过类型实例block调用完成两个Int类型的值求和。通常对一个高阶函数的调用更习惯于用匿名Lambda表达式,匿名Lambda表达式和Java中匿名类如出一辙。java中用关键字new创建一个匿名类,Kotlin中省略了创建对象的关键字。

kotlin 复制代码
fun test() {
    val left = 1
    val right = 2
    val result = sum(left,right,{left: Int, right: Int -> left + right})
    println(result)
}
fun sum(left:Int,right:Int,block:(Int,Int)->Int):Int{
    return block.invoke(left,right)
}

拖尾Lambda表达式

如果最后一个参数是函数,那么作为相应的参数传入的lambda表达式可以放到圆括号之外

kotlin 复制代码
fun test() {
    val left = 1
    val right = 2
    val result = sum(left,right) { left: Int, right: Int -> left + right }
    println(result)
}
fun sum(left:Int,right:Int,block:(Int,Int)->Int):Int{
    return block.invoke(left,right)
}

如果高阶函数只有一个函数类型的参数,在调用时可以省略(),直接在函数名后加花括号{}

kotlin 复制代码
fun test() {
    n{ println("n call") }
}
fun n(block:()->Unit){
    block.invoke()
}

带上下文作用域Lambda表达式

A.(B)->C。在A类中定义了一个(B)->C的函数类型

kotlin 复制代码
fun test() {
    val lan = "kotlin"
    lan.info()
}
val info:String.() -> Unit = {
    val length = this.length
    val isEmpty = this.isEmpty()
    val hashCode = hashCode()
    println("len=$length isEmpty=$isEmpty hashCode=$hashCode")
}

给String类定义了"扩展函数类型"的属性info,输出当前字符串信息。并使用Lambda表达式进行了初始化。花括号中直接使用隐式的this来访问当前接受者。 可以在Lambda的{}中像接受者的内部一样,访问接受者任意公开的方法和属性。

拥有函数返回值的函数

kotlin 复制代码
fun test() {
    val left = 1
    val right = 2
    val result = sum().invoke(left,right)
    println(result)
}

fun sum():(Int,Int)->Int{
    val block ={left:Int,right:Int -> left+right}
    return block
}

sum函数返回的是一个函数类型:(Int,Int)->Int的实例,一个匿名Lambda表达式。执行函数类型中函数体的代码,需要对这个函数类型实例再进行一次调用

kotlin 复制代码
val result = sum().invoke(left,right)
// 或者
val result2 = sum()(left,right)

官方源码中Suspend.kt提供了suspend

kotlin 复制代码
public inline fun <R> suspend(noinline block: suspend () -> R): suspend () -> R = block

suspend关键字修饰的函数或函数类型的参数称挂起函数,一般在协程中用到。

函数式接口

只有一个抽象方法的接口,或单一抽象方法接口

java 复制代码
public interface OnClickListener{
    void onClick(View v);
}

java中给一个view设置点击事件,会new关键字创建匿名类

java 复制代码
view.setOnclickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v){...}
});

Kotlin中创建一个匿名类使用关键字object

kotlin 复制代码
view.setOnClickListener(object:View.OnClickListener){
    override fun onClick(v:View){...}
}

可以转换成函数类型(View) -> Unit

kotlin 复制代码
// java接口
public interface OnClickListener{
    void onClick(View v);
}
// kotlin函数类型
(View) -> Unit
kotlin 复制代码
// java匿名类
new View.OnClickListener(){
    @Override
    public void onClick(View v){}
}

// kotlin Lambda表达式
{view:View-> }

简化上面点击事件

kotlin 复制代码
view.setOnClickListener({view:View -> println("click")})

Lambda表达式该方法唯一参数,可以省略(),将花括号直接放到方法名后。

kotlin 复制代码
view.setOnClickListener{ view:View -> println("click")}

函数类型只有一个参数,可以在Lambda表达式中省略参数和声明->。参数隐式声明为it

kotlin 复制代码
view.setOnClickListener{println(it)}

实现自己的let方法

可以实现标准库Standard.kt中的一些高阶函数。let函数可以让任意对象在任意地方调用。let函数首先定义一个泛型函数且是一个扩展函数,可以在任意地方调用,let函数一定是顶层函数

kotlin 复制代码
fun <T> T.let(){}

let函数是一个拥有函数类型的参数

kotlin 复制代码
fun <T> T.let(block:()->Unit){}

Lambda表达式获取调用者的隐式名it。let函数中拥有一个自己的参数,参数是调用者自己。

kotlin 复制代码
fun <T> T.let(block:(T)->Unit){}

标注看Standard.kt中还有apply also with函数

总结

Kotlin一些简化写法一般会在函数中出现,方便扩展一些自己常用的一些工具集。

相关推荐
DT——1 小时前
Vite项目中eslint的简单配置
前端·javascript·代码规范
OkeyProxy6 小时前
設置Android設備全局代理
android·代理模式·proxy模式·代理服务器·海外ip代理
刘志辉6 小时前
vue传参方法
android·vue.js·flutter
前期后期9 小时前
Android OkHttp源码分析(一):为什么OkHttp的请求速度很快?为什么可以高扩展?为什么可以高并发
android·okhttp
轻口味11 小时前
Android应用性能优化
android
全职计算机毕业设计11 小时前
基于 UniApp 平台的学生闲置物品售卖小程序设计与实现
android·uni-app
dgiij12 小时前
AutoX.js向后端传输二进制数据
android·javascript·websocket·node.js·自动化
SevenUUp13 小时前
Android Manifest权限清单
android
高林雨露13 小时前
Android 检测图片抓拍, 聚焦图片后自动完成拍照,未对准图片的提示请将摄像头对准要拍照的图片
android·拍照抓拍
wilanzai13 小时前
Android View 的绘制流程
android