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

相关推荐
Ryannn_NN36 分钟前
avalonia android连接模拟器时报错adb cannot run as root in production builds,没有权限
android·adb·wpf·xamarin
ii_best1 小时前
按键精灵ios/安卓辅助工具高级函数OcrEx文字识别(增强版)脚本开发介绍
android·ios
_龙小鱼_1 小时前
Android日活(DAU)检测的四大实现方案详解
android
姜行运1 小时前
数据结构【AVL树】
android·数据结构·c#
云手机管家5 小时前
自动化脚本开发:Python调用云手机API实现TikTok批量内容发布
android·网络安全·智能手机·架构·自动化
咕噜企业签名分发-淼淼6 小时前
iOS苹果和Android安卓测试APP应用程序的区别差异
android·ios·cocoa
IT从业者张某某6 小时前
信奥赛-刷题笔记-栈篇-T2-P1165日志分析0519
android·java·笔记
androidwork7 小时前
Kotlin与物联网(IoT):Android Things开发探索
android·物联网·kotlin
橙子199110168 小时前
在 Kotlin 中,什么是内联函数?有什么作用?
android·开发语言·kotlin
悠哉清闲8 小时前
Kotlin 协程 (一)
android·开发语言·kotlin