前一段時(shí)間跟一個(gè)朋友聊天聊到run,apply里面有一個(gè)隱蔽的陷阱,記錄下來(lái)吕世,前車之鑒,后車之師
泛型T
首先我們先看一下apply的源碼
fun T.apply(block: T.() -> Unit): T { block(); return this }
這個(gè)泛型T可以為null,也就是說(shuō)null也被賦予了apply的方法
null.apply{
System.out.println("null apply")
}
在IDE里面null是沒(méi)有.apply方法提示的梯投,可是在kotlin編譯里面是可以通過(guò)
null埋藏的陷阱
由于null也有apply方法命辖,所以對(duì)一個(gè)可空的對(duì)象(A)進(jìn)行apply的話,block塊是被執(zhí)行的分蓖。如果A的方法與全局方法有重名的時(shí)候就會(huì)調(diào)用全局的方法尔艇。
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還有繼承關(guān)系那么就更加的隱蔽
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泛型的擴(kuò)展
利用泛型的擴(kuò)展我可以封裝一個(gè)nullwork方法,就是一個(gè)對(duì)象為空時(shí)執(zhí)行nullblock,如果對(duì)象不為空時(shí)執(zhí)行noNullblock么鹤。
inline fun <T, R> T?.nullWork(noNull: (T) -> R, isNull: () -> R): R {
return this?.let {
//這里還有一個(gè)坑终娃,就是如果block返回null還是會(huì)執(zhí)行isNull()方法所以要使用return
return noNull(it)
} ?: isNull()
}
總結(jié)
陷阱的關(guān)鍵點(diǎn)有2個(gè)
- null對(duì)象有apply方法導(dǎo)致null對(duì)象的apply的block塊是被執(zhí)行的
- blcok塊可以調(diào)用全局方法
前車之鑒,后車之師蒸甜,大家共勉棠耕。