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一些简化写法一般会在函数中出现,方便扩展一些自己常用的一些工具集。

相关推荐
Digitally1 小时前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone
whysqwhw2 小时前
OkHttp深度架构缺陷分析与演进规划
android
用户7093722538512 小时前
Android14 SystemUI NotificationShadeWindowView 加载显示过程
android
木叶丸3 小时前
跨平台方案该如何选择?
android·前端·ios
顾林海3 小时前
Android ClassLoader加载机制详解
android·面试·源码
用户2018792831673 小时前
🎨 童话:Android画布王国的奇妙冒险
android
whysqwhw4 小时前
OkHttp框架的全面深入架构分析
android
你过来啊你4 小时前
Android App冷启动流程详解
android
泓博5 小时前
KMP(Kotlin Multiplatform)改造(Android/iOS)老项目
android·ios·kotlin
移动开发者1号5 小时前
使用Baseline Profile提升Android应用启动速度的终极指南
android·kotlin