kotlin lambda简介
一、lambda表达式的格式
这里介绍该表达式的几个用法
data class Person(val name: String, val age: Int) val peoples = listOf(Person("aa", 29), Person("bb", 30)) println(peoples.maxBy( { p: Person -> p.age })) // kotlin语法约定,如果lambda表达式是函数调用的最后一个实参,可以放到括号的外边 println(peoples.maxBy() { p: Person -> p.age }) // 当lambda是函数的唯一实参时,可以去掉空括号 println(peoples.maxBy { p: Person -> p.age }) // 如果lambda的参数能够推理出对应的类型,则可以把参数的类型去掉 println(peoples.maxBy { p -> p.age }) // 当lambda表达式只有一个的时候,可以不明确指定名称,默认会是it println(peoples.maxBy { it.age }) // 但是要注意的是,如果是嵌套的lambda时候,最好显示地声明每个lambda的参数,避免搞混it到底是哪个值
如上是调用maxBy的几种方式,如下是maxBy需要传入的参数,是一个函数的类型:一个传入参数和一个返回参数
maxBy(selector: (T) -> R)
二、作为函数的代码块
java的接口
public interface OnClickListener { void onClick(String v); }
1、使用java的方式进行创建匿名内部类的方式进行继承
OnClickListener listener = new OnClickListener() { @Override public void onClick(String v) { // 这里实现OnClickListener的函数 System.out.println("new OnClickListener():" + v); } }; listener.onClick("kk"); //输出:new OnClickListener():kk
2、使用java的方式进行创建lambda
OnClickListener listenerLambda = v -> { System.out.println("lambda:" + v); }; listenerLambda.onClick("qqq"); // 输出:lambda:qqq
java在使用lambda在继承方面看似也不错,很简洁
kotlin的接口
interface OnClickListener1 { fun onClick(v: String) } fun testClick2(clickListener1: OnClickListener1) { clickListener1.onClick("kkqqq") }
3、使用kotlin匿名内部类的方式
testClick2(object: OnClickListener1 { override fun onClick(v: String) { println("object: OnClickListener1:${v}") } }) // 输出:object: OnClickListener1:kkqqq
看着跟java的匿名内部类差不多
4、使用kotlin的方式创建lambda
这边在使用kotlin的lambda调用上述的testClick2,按照正常的想法应该是能够正确推理出:OnClickListener1 接口
testClick2 { println("lambda interface:$it")} // 这样子调用老是提示:Type mismatch.
这是为啥呢?
这里先改变一下testClick2的参数为上述java的接口
fun testClick1(clickListener: OnClickListener) { // OnClickListener是上述的java的接口 clickListener.onClick("kkqqq") } testClick1 { println("lambda interface:$it")} // 能正确输出:lambda interface:kkqqq
为啥使用java的接口可以但是kotlin的接口就不行呢?
官方的解释是 Kotlin 本身已经有了函数类型和高阶函数等支持,所以不需要再去转换。如果你想使用类似的需要用 lambda 做参数的操作,应该自己去定义需要指定函数类型的高阶函数。
三、SAM转换的概念
SAM转换的英文全拼是:Single Abstract Method Conversions。就是对于只有单个非默认抽象方法接口的转换 ,对于符合这个条件的接口在 Kotlin 中可以直接用 Lambda 来表示 (前提是 Lambda 的所表示函数类型能够跟接口中的方法相匹配)
例子:
上述的testClick1对应的接口是OnClickListener,对testClick1的传入参数是lambda表达式,该lambda表达式的类型是:(String) -> Unit , 正好跟OnClickListener接口的单一方法接口类型一样:void onClick(String v) ;所以testClick1的调用能够通过 :{ println("lambda interface:$it")} 替换OnClickListener作为testClick1的参数
如果SAM转换存在歧义该如何消除
1、这里有两个java接口
public interface OnClickListener { void onClick(String v); } public interface OnClickListener2 { void onClick(String v); }
还有两个对应的kotlin的函数,参数分别是上述的OnClickListener和OnClickListener2
fun testClick1(clickListener: OnClickListener) { clickListener.onClick("kkqqq") } // 这里使用重载testClick1函数 fun testClick1(clickListener: OnClickListener2) { clickListener.onClick("kkqqq") }
如果直接调用:testClick1 {println("lambda interface:$it")} 则会提示:Overload resolution ambiguity.
这里的正确调用:
方法一:使用lambda的方式:testClick1 (OnClickListener{println("lambda interface:$it")}) 该方法首先创建了一个`OnClickListener`的匿名实例,然后对其唯一的方法进行了实现。
方法二:使用匿名的内部类方式
testClick1(object: OnClickListener { override fun onClick(v: String?) { println("lambda interface:$v") } })
方法三、testClick ({it: String -> println("lambda interface:$it")} as OnClickListener) 但是这个方法在运行的时候提示:cannot be cast to class OnClickListener。原因是因为:在Kotlin中,不能直接将Lambda表达式强制类型转换为Java SAM接口,即使该接口是一个函数接口
四、捕捉作用域中的变量
1、java中lambda要使用外部的局部变量需要是final才可行
2、kotlin中的lambda可以随便使用外部的局部变量,并且可以在lambda中修改该局部变量
fun updateLocalCount(): () -> Int { var count = 0 val inc = { count+=1 // 在lambda内部将count加1 count // 在这里返回改变count后的值 } count += 1 // 在外部加1 count=1 inc() // 这里调用这个lambda之后,count=2 count += 1 // count=3 println(count) // 这里输出的是3 return inc } val inc = updateLocalCount() // 将updateLocalCount函数内部的lambda返回 println(inc()) // inc调用完之后count从3变成了4,从而这里输出的是:4
因为这里count是非final,它的值被inc捕获,则它的值被封装在一个特殊的包装器中,这样子inc内部就可以改变这个值
五、成员引用
如下是成员引用的格式,可以是成员变量也可以是成员函数
如下给出成员引用和全局函数的引用
data class Person(val name: String, val age: Int) class User { fun printName(name: String) { println("username:${name}") } } val p = Person("kk", 33) val personAgeFunction = Person::age //这里personAgeFunction的类型为: KProperty1<Person, Int> 即:入参是Person,返回是Int println(personAgeFunction(p)) // val dimAgeFunction = p::age // 这里dimAgeFunction的类型为:KProperty0<Int> println(dimAgeFunction()) val user = User() val userNameFunc = User::printName // 如果本身有参数,那么在入参是User之后还要增加printName的传入参数String即: KFunction2<User, String, Unit> println(userNameFunc(user, "kk")) val dimNameFunc = user::printName // 同理这里只需要 dimNameFunc("qqq") val gFunc = ::testGlobalFunc // 因为是全局的函数,所以这里没有成员引用的前缀。 这里的gFunc的类型为: KFunction0<Unit> gFunc()
如上有3种格式, 注意都有冒号
1、类名::类成员
2、类的实例::类成员
3、::全局函数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!