Kotlin之Lambda表达式原理及应用
来源:互联网 发布:红色痘印怎么消除 知乎 编辑:程序博客网 时间:2024/06/08 13:27
Kotlin之Lambda表达式原理及应用
初探
Lambda表达,可以看成是一个代码块,先来一个栗子热热身.
val codeblock = { f: Float, s: String -> println("hello $s") f.toByte() }
- 以上的codeblock就是一个代码块,也可以理解成一个方法,它的两个参数分别为Float,String
- 它的返回类型是Byte
val codeblock暴露了它是一种类型,那它是什么类型呢?打印它
println(codeblock.javaClass)输出:class com.seekting.kotlindemo.PersonKt$main$codeblock$1
我们可以理解成它是一个内部类,且名字叫1
那它有什么方法呢?打印它
for (m in codeblock.javaClass.methods) { println(m.name) } 输出: invoke invoke toString getArity wait wait wait equals hashCode getClass notify notifyAll
你会发现比其它类多了些什么,那我新建一个Test类,看看它会有什么方法
class Test() {} val t = Test() for (m in t.javaClass.methods) { println("test:${m.name}") }打印结果:test:waittest:waittest:waittest:equalstest:toStringtest:hashCodetest:getClasstest:notifytest:notifyAll
可以看出多了三个方法getArity,invoke,invoke
从这点可以看出,lambda表达就是一个类,它有invoke方法,那我们通过反射调一下吧
for (m in codeblock.javaClass.methods) { if (m.name == "invoke") { val a = m.invoke(codeblock, 1.2f, "tt") println("a=$a") } else if (m.name == "getArity") { val a = m.invoke(codeblock) println("a=$a") } } 输出: hello tt a=1 hello tt a=1 a=2
通过输出发现调两个invoke调用了代码块:
println("hello $s") f.toByte()
并通过代码块返回了1
那getArity返回2是几个意思年,arity是参数数量的意思,难道和参数列表有关?改代码块试试
for (m in codeblock.javaClass.methods) { if (m.name == "invoke") { val a = m.invoke(codeblock, 1.2f, "tt", true) println("a=$a") } else if (m.name == "getArity") { val a = m.invoke(codeblock) println("a=$a") } }输出:hello tt,b=truea=1hello tt,b=truea=1a=3
果然它是参数个数的意思
总结一下,代码块可以理解成java的一个类,且有invoke方法
看看这一行代码:
val result: Byte = codeblock(1.2f, "3")
以上代码就是调用了该对象的invoke方法而已。
进阶:
我们知道一个方法,它的参数可以是任意类型,那Lambda也是一种类型(变相类型)
申明一个mn方法,它有Int m,Int n,Lambda block三个形参,返回Boolean
你可以认为block是一个类型,此类型有一个invoke方法,它接收Float,String类型的参数,返回Byte (没绕进去吧我的哥)
inline fun mn(n: Int, m: Int, block: (Float, String) -> Byte): Boolean { block(3.14f, "PI")//其实是调用对象的invoke方法 return m * n > 0}mn(1, 2, { f: Float, s: String -> val result = "i=${f}s=$s" print(result) f.toByte()//最后一行就是返回})
- mn为一个方法Boolean是它的返回
- mn有三个参数:m,n,代码块block
- 代码块的的返回类型是Byte类型
- 这个代码块需要调用的人实现,但是这个代码块依赖两个参数Float,String
- 实现block代码块的人会得到Float,String参数,可能通过这两个参数操作,为result服务
- block的Float,String两个参数由mn方法提供
解迷Standard.kt里的一些函数库
run()函数
run { println("run!") }
这是系统里自带的库函数,看起来挺厉害的样子,来分析一下怎么实现的吧
@kotlin.internal.InlineOnlypublic inline fun <R> run(block: () -> R): R = block()
- 泛型了一个R
- run方法有一个变相类型(Lambda)
- R=block()可以理解为
{ return block.invoke()}
你一定会好奇:run方法明明有一个参数,为何不见了呢?
如果最后一个参数是block()可以用大括号括起来.我们也可以写一个这样的方法myRun
fun myRun(t: String, block: (String) -> Unit) { block(t)} myRun("hello") { println(it) }
但是如果是这样呢?它要两个block,这个时候就只能是第一个block1通过传参数
fun myRun1(t: String, block: (String) -> Unit, block1: (String) -> Unit) { block(t)} val block1 = { s: String -> println(s) }myRun1("hello", block1) {}
let()函数
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
- T,R泛型,代表任何对象都可以调用let方法
- let方法有一个参数是变相类型(Lambda)参数是T,返回是R
- 方法实体是调用了这个Lambda的invoke方法,并把T当成参数传进去
以下是let的例子
"seekting".let { println(it.get(0)) } val person = Person1("seekting") person.let { it.say() it.sleep() it.wakeup() }
with()函数
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
- 泛型T,R
- 第一个参数是一个对象,第二个参数是(Lambda)该Lambda有点特别:
block: T.() -> R
T.()这个代码初看无法理解。通过IDE我偶然发现了,其实T.()是T类的方法的扩展,还记得有这样的代码
public inline fun String.toUpperCase(): String
它扩展了String的方法,但是这个方法不是String类自己的,可以理解成
public static String toUpperCase(String input){}
我用block:Person.()来打比方,它只有一个函数print
class Person( var id: Long, var name: String, var age: Int) { fun print() { var ss = toString() Log.d("seekting", ss) }}fun study(person: Person, block: Person.() -> Unit) { person.print() person.block()}
但是在study方法里可以调用Person.block方法,说明什么?
说明这个Person扩展了一个方法叫block,而它的实现:
study(person1) { person1.age = 11 person1.name = "11" }
你可以理解为有一个方法:
fun Person.block(){ this.age = 11 this.name = "11"}
回过头来看with
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()with(person){ person1.age = 11 person1.name = "11"}
- 它有一个T类型,并扩展了一个函数叫block
- receiver.block()表示t调用了一个扩展了的函数(但只在with里可见)
- 而这个block()函数怎么实现的,外部实现的;实现体就是
person1.age = 11person1.name = "11"
apply()函数
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
- 任意对象都有一个apply方法,还有一个block方法(只在apply里可见)
- apply方法调用block方法,block方法由外部自己实现,实现完返回该对象
- 对比let:任意对象(T)有一个let方法,该方法能把T对象当成参数处理并返block的返回类型
- 它们的共同点都会调用block代码块,但是不一样的是:let返回的是block返回类型,apply返回的是原对象
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
also()函数,apply和also的区别
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
also和apply类似,唯一不一样的是:
apply是调用对象的扩展block()方法,因此在block里可以用this,而不能用it
而also是调用一个block(T t)方法,因此block里不能用this,而能用it
also 与apply的不同之处:
person1.apply { this.age }person1.also { it.age = 11}person1.also { p: Person -> p.age = 11 //this.age=11 报错}
- Kotlin之Lambda表达式原理及应用
- kotlin lambda表达式
- kotlin的lambda表达式
- Kotlin高阶函数及Lambda表达式解析
- Kotlin编程之高阶函数,Lambda表达式,匿名函数
- Kotlin学习之-6.2 高阶函数和lambda表达式
- kotlin入门(九)Lambda表达式
- Kotlin的Lambda表达式小结
- JAVA8之lambda表达式详解,及stream中的lambda使用
- JAVA8之lambda表达式详解,及stream中的lambda使用
- JAVA8之lambda表达式详解,及stream中的lambda使用
- JAVA8之lambda表达式详解,及stream中的lambda使用
- 【C#】之匿名函数及Lambda表达式
- kotlin学习之自定义lambda表达式----简单易懂教你写一个可以接受lambda表达式的函数
- Lambda表达式 推算原理
- java8 lambda表达式原理
- java8 lambda表达式原理
- Kotlin 高阶函数 与 Lambda 表达式
- python pip安装方法
- matlab 如何创建多个legend
- 【日期类】C++实现
- git 遇到的问题
- VirtualAPK框架接入
- Kotlin之Lambda表达式原理及应用
- MySQL 自动编号与主键约束
- C++17尝鲜:结构化绑定声明(Structured Binding Declaration)
- java的Object类
- LeetCode 454. 4Sum II (Medium)
- 最透彻的关于“随机数种子”和“伪随机数”的产生原理
- Spring Boot与Log4j2集成之java.lang.IllegalStateException: Logback configuration error detected:
- 过滤器和拦截器的区别
- [Scala--基础]---Scala枚举使用