Kotlin难点解析:extension和this指针
擴(kuò)展函數(shù)難點(diǎn)解析
大多數(shù)場(chǎng)景下,你都能輕松搞定Kotlin擴(kuò)展。可是,看看下面這個(gè)題目,你還能脫口而出,告訴我答案是什么嗎?
open class E {}open class E1: E() {}open class A {open fun E.f() {println("E.f in A")}open fun E1.f() {println("E1.f in A")}fun call(e: E) {e.f()} }class A1: A() {override fun E.f() {println("E.f in A1")}override fun E1.f() {println("E1.f in A1")} }fun main(args: Array<String>) {// a)A().call(E())// b)A1().call(E())// c)A().call(E())// d)A().call(E1()) }問題:請(qǐng)告訴a,b,c,d位置代碼執(zhí)行的輸出結(jié)果是什么?
對(duì)于這個(gè)問題,恐怕你在紙上寫寫畫畫半天也不一定能給出正確答案吧。關(guān)于這個(gè)問題,其實(shí)我之前的一篇文章 [ [Kotlin] Lambda and Extension](https://www.jianshu.com/p/d7a... 中有提到過。可是,我認(rèn)為這篇文章關(guān)于這部分的解釋不夠清晰,有必要再詳細(xì)闡述一次。
Ok,let's started。
為了解決這個(gè)問題,官方提出了兩個(gè)新的概念:dispatch receiver和extension receiver。
- dispatch receiver:中文翻譯為分發(fā)接收者。所謂的分發(fā)接收者,就是聲明這個(gè)擴(kuò)展方法所在的類。即:在哪個(gè)類中聲明,那個(gè)類就是你的分發(fā)接收者。
- extension receiver:中文翻譯為擴(kuò)展接收者。所謂的擴(kuò)展接收者,就是你實(shí)際擴(kuò)展的那個(gè)類。舉個(gè)例子:你針對(duì)Int類擴(kuò)展了一個(gè)方法add,這個(gè)add方法的擴(kuò)展接收者就是Int類實(shí)例。
為了簡(jiǎn)化,這里我們將dispatch receiver簡(jiǎn)稱為DR,將extension receiver簡(jiǎn)稱為ER。
還記得多態(tài)的概念嗎?多態(tài)是一種運(yùn)行時(shí)概念,即對(duì)象的類型要等到運(yùn)行時(shí)才能最終確定。因此,一些語言中也將多態(tài)叫做類型延遲加載。解決上面這個(gè)問題我們需要關(guān)注就是擴(kuò)展函數(shù)是否會(huì)產(chǎn)生多態(tài)行為。
這里我們將產(chǎn)生多態(tài)行為的技術(shù)叫做動(dòng)態(tài)解析,與之相反的行為稱之為靜態(tài)解析。
為了解決上面的問題,你需要記住下面這個(gè)規(guī)則:
- DR類型是動(dòng)態(tài)解析的
- 與之相反,ER類型是靜態(tài)解析的
先看上面例子的a、b部分,很顯然:
- a代碼中f函數(shù)的DR是類A,ER是類E
- b代碼中f函數(shù)的DR是類A1,ER是類E
參照上面的規(guī)則,由于DR類型是動(dòng)態(tài)解析的。在A1類中我們重寫了E的擴(kuò)展函數(shù)f,運(yùn)行時(shí)最終會(huì)執(zhí)行A1類中擴(kuò)展的f方法。a部分很明顯會(huì)輸出A類中擴(kuò)展的f方法。因此,最終的輸出結(jié)果如下:
E.f in A E.f in A1繼續(xù)看c、d部分,c、d部分的DR都是A,而對(duì)于ER,c、d分別是E、E1。參照上面的規(guī)則,ER是靜態(tài)解析的。在call方法聲明的地方,我們傳入的對(duì)象類型是E,這就決定了無論擴(kuò)展方法是來自E還是其子類,將始終執(zhí)行E類的擴(kuò)展方法。因此,c、d部分將輸出同樣的結(jié)果:
E.f in A E.f in A由此可見,如果你牢記上述兩條規(guī)則,解決問題將變得非常容易。為了加強(qiáng)你的記憶,我用一個(gè)表格總結(jié)上面的知識(shí)點(diǎn):
| 概念 | 擴(kuò)展方法聲明所在的類 | 聲明擴(kuò)展方法的類 |
| 解析方式 | 動(dòng)態(tài)解析 | 靜態(tài)解析 |
PS:由于新版本Kotlin中針對(duì)擴(kuò)展函數(shù)也加入了override關(guān)鍵字,這非常有助于DR和ER的理解。如果你在使用Kotlin,強(qiáng)烈建議你更新到最新版本。
不太一樣的this指針
在Java語言中,如果你在內(nèi)部類中需要外部類的引用可以將this寫在類名后面。可是,試試看Kotlin,果斷不行。
為了獲得外部類的引用,Kotlin語言引入了@符號(hào)。舉個(gè)例子:
class Outer {inner class Inner {fun f() {println(this@Outer)}} }可以看到,為了獲取外部類的引用,只需要在@后面接外部類的名稱即可。
如果對(duì)應(yīng)一個(gè)擴(kuò)展函數(shù),this引用指向是什么呢?先說答案,擴(kuò)展函數(shù)中的this指針指向ER,即實(shí)際擴(kuò)展的那個(gè)類對(duì)象。
fun Outer.foo() {println(this) }這里的this指向foo函數(shù)的接收者Outer類實(shí)例。
this指針還有一種場(chǎng)景是用在lambda表達(dá)式中,這是一種比較特殊的使用場(chǎng)景。lambda表達(dá)式本身沒有任何接收者,如果是在全局聲明一個(gè)lambda表達(dá)式,將不能使用this指針。而如果是在某個(gè)類或者擴(kuò)展方法中使用this指針,將指向?qū)嶋H所在類或者擴(kuò)展方法的接收者。
如果你習(xí)慣了Kotlin語言的這種表達(dá)方式,this指針的指向就不再是一個(gè)問題了。在你習(xí)慣這種用法之前,我用一個(gè)表格簡(jiǎn)單總結(jié)一下this指針的用法:
| 類中 | 默認(rèn)指向當(dāng)前類實(shí)例,使用@操作符指向具體外部類實(shí)例 |
| 擴(kuò)展函數(shù) | 默認(rèn)指向擴(kuò)展函數(shù)的接收者 |
| lambda表達(dá)式 | 默認(rèn)指向?qū)嶋H所在類實(shí)例或所在擴(kuò)展函數(shù)的接收者 |
總結(jié)
關(guān)于擴(kuò)展,大多數(shù)情況下,你不會(huì)遇到文章開頭那種復(fù)雜的情況。如果遇到了這種情況,只要清楚地區(qū)分DR和ER,并牢記DR和ER的解析方式,就能輕松應(yīng)對(duì)了。對(duì)于this指針,與Java語言不一樣的地方是,為了引用具體類的實(shí)例,Kotlin語言使用@符號(hào)。個(gè)人認(rèn)為,這種表述方式更自然。如果遇到某些比較復(fù)雜的情況,只需要弄清楚接收者,問題就引刃而解了。
歡迎加入Kotlin交流群
如果你也喜歡Kotlin語言,歡迎加入我的Kotlin交流群: 329673958 ,一起來參與Kotlin語言的推廣工作。
編程,我們是認(rèn)真的!
關(guān)注歐陽鋒工作室公眾號(hào),你想要的都在這里:
總結(jié)
以上是生活随笔為你收集整理的Kotlin难点解析:extension和this指针的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot微信点餐——实战开
- 下一篇: 运行期优化