前一段时间跟一个朋友聊天聊到run,apply里面有一个隐蔽的陷阱,记录下来,前车之鉴,后车之师
泛型T
首先我们先看一下apply的源码
fun T.apply(block: T.() -> Unit): T { block(); return this }
这个泛型T可以为null,也就是说null也被赋予了apply的方法
null.apply{
System.out.println("null apply")
}
在IDE里面null是没有.apply方法提示的,可是在kotlin编译里面是可以通过
null埋藏的陷阱
由于null也有apply方法,所以对一个可空的对象(A)进行apply的话,block块是被执行的。如果A的方法与全局方法有重名的时候就会调用全局的方法。
open class A {
override fun toString(): String {
return "this is A"
}
open fun methodPrint() {
System.out.println("A print")
}
}
class B {
override fun toString(): String {
return "this is B"
}
fun methodPrint() {
System.out.println("B print")
}
fun print() {
val a: A? = null
System.out.println(a.apply {
methodPrint()
System.out.println("a.apply:" + toString())
})
}
}
输出
B print
a.apply:null
null
如果B还有继承关系那么就更加的隐蔽
class C : A(){
override fun toString(): String {
return "this is C"
}
fun print() {
val a: A? = null
System.out.println(a.apply {
methodPrint()
System.out.println("a.apply:" + toString())
})
}
}
输出
A print //此处打印的是C类的methodPrint()方法继承于A
a.apply:null
null
T泛型的扩展
利用泛型的扩展我可以封装一个nullwork方法,就是一个对象为空时执行nullblock,如果对象不为空时执行noNullblock。
inline fun <T, R> T?.nullWork(noNull: (T) -> R, isNull: () -> R): R {
return this?.let {
//这里还有一个坑,就是如果block返回null还是会执行isNull()方法所以要使用return
return noNull(it)
} ?: isNull()
}
总结
陷阱的关键点有2个
- null对象有apply方法导致null对象的apply的block块是被执行的
- blcok块可以调用全局方法
前车之鉴,后车之师,大家共勉。
网友评论