最近劲妙,一個同學突然問到一個問題:為什么擴展屬性不能直接進行初始化湃鹊?針對這個問題,官方文檔有一段簡短的解釋镣奋,翻譯成中文就是:實際上币呵,擴展并不會真正地往類中插入成員變量。因此侨颈,我們沒有一個有效的方式讓一個擴展屬性擁有backing field余赢,這就是擴展屬性不允許被初始化的原因。大多數(shù)同學看到上面這一段話肯定是一頭霧水哈垢,但我們抓到了一個關(guān)鍵詞backing field妻柒。解釋這個問題,其實就是理解backing field的本質(zhì)耘分。
backing field是什么
這個單詞不好翻譯举塔,很多譯文將其翻譯為幕后屬性或幕后字段。的確求泰,有那么一層意思央渣,但總感覺不是很貼切。因此渴频,這篇文章我們始終使用這個官方英文單詞來表示芽丹。
在Kotlin語言中,如果在類中定義一個成員變量卜朗,Kotlin將自動生成默認setter/getter方法拔第。而Kotlin提供了一種非常特殊的方式聲明setter/getter方法:
var name: String? = null
set(value) {
field = value
}
get() = field
這里我們直接使用了本次的主人公field字段,如果不使用會怎樣呢场钉?你應(yīng)該很自然地想到這樣處理:
var name: String? = null
set(value) {
name = value
}
get() = name
實例化這個類楼肪,然后對當前實例的name屬性進行賦值并取值。Oops...結(jié)果惹悄,你會發(fā)現(xiàn)春叫,無論是取值還是賦值都出現(xiàn)遞歸調(diào)用。
這是為什么呢?是的暂殖,聰明的你肯定已經(jīng)想到了价匠。我們在setter方法中對name賦值的時候會調(diào)用自身,結(jié)果出現(xiàn)了遞歸調(diào)用呛每,getter方法同理踩窖。
這個時候救世主backing field降臨了。backing field的作用域僅僅存在于當前屬性的setter/getter方法中晨横,它就像當前屬性的影子一樣洋腮。因此,我認為翻譯成影子屬性也許更合適手形。
理解了backing field的意圖之后啥供,我們再來解釋為什么擴展屬性不能直接初始化。這個時候库糠,使用正向思維直接推導就可以了伙狐。假設(shè)對Dog
擴展了一個屬性color
,假設(shè)可以直接初始化瞬欧,聲明看起來應(yīng)該是這樣:
var Dog.color: String = "#ff0000"
注意:Kotlin語言針對屬性默認會生成setter/getter方法贷屎,其默認實現(xiàn)是這樣的:
var Dog.color: String = "#ff0000"
set(value) field = value
get() = field
我們看到了熟悉的老朋友field
,由于擴展屬性并不會真正地在類中插入字段艘虎,那這個field自然就無處安放了唉侄。有人說,可以放在全局嗎野建?
當然不行美旧!field
是什么?field
是當前屬性的影子贬墩。而當前屬性是與某個實例一一對應(yīng)的榴嗅。這就是為什么官方文檔說,沒有一個有效的辦法使擴展屬性擁有field字段陶舞。結(jié)果就尷尬了嗽测!聲明擴展屬性的時候我們就不得不自己實現(xiàn)setter/getter方法。
這里大家還可以做一個簡單的實驗肿孵,使用上述的定義方式唠粥,看看最終Kotlin幫忙生成的setter/getter代碼是什么。這里我直接將實驗結(jié)果展示給大家看:
@Nullable
private String name;
@Nullable
public final String getName() {
return this.name;
}
// 這里的value對應(yīng)你在set方法中自定義的參數(shù)名稱
public final void setName(@Nullable String value) {
this.name = value;
}
簡單總結(jié)
至此停做,整個問題的謎底已經(jīng)揭曉了晤愧。由于Java語言中不存在backing field這樣一個屬性,難免會導致理解上的問題蛉腌。這個時候可以使用逆推 + 猜想的方式尋找答案官份。文章最后我給出了Kotlin幫忙生成的代碼片段只厘,這是怎么做到的呢?其實很簡單舅巷,先使用Kotlin編譯器將Kotlin代碼編譯成Java字節(jié)碼羔味,再用JD-GUI反編譯class文件就可以看到最終生成的Java代碼了。
我是歐陽鋒钠右,我熱愛Kotlin赋元。如果你喜歡我的文章,請在文章下方留下你愛的小心心飒房。如果你不喜歡我的文章搁凸,請先喜歡上我的文章。然后再留下愛的小心心狠毯!
下次文章再見护糖,拜拜!