Lambda表达,可以看成是一个代码块,先来一个栗子热热身.
val codeblock = { f: Float, s: String ->
println("hello $s")
f.toByte()
}
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:wait
test:wait
test:wait
test:equals
test:toString
test:hashCode
test:getClass
test:notify
test: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=true
a=1
hello tt,b=true
a=1
a=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()//最后一行就是返回
})
run {
println("run!")
}
这是系统里自带的库函数,看起来挺厉害的样子,来分析一下怎么实现的吧
@kotlin.internal.InlineOnly
public inline fun run(block: () -> R): 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) {
}
public inline fun T.let(block: (T) -> R): R = block(this)
"seekting".let {
println(it.get(0))
}
val person = Person1("seekting")
person.let {
it.say()
it.sleep()
it.wakeup()
}
public inline fun with(receiver: T, block: T.() -> R): R = receiver.block()
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 with(receiver: T, block: T.() -> R): R = receiver.block()
with(person){
person1.age = 11
person1.name = "11"
}
person1.age = 11
person1.name = "11"
public inline fun T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun T.let(block: (T) -> R): R = block(this)
public inline fun 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 报错
}