本章節(jié)來介紹Kotlin面向對象的高級部分:
一. 擴展:kotlin的擴展是一個很獨特的功能醒叁,java本身支持擴展亮曹,Kotlin為了讓擴展能在JVM平臺上運行,必須做了一些獨特的處理淤毛。
1.擴展方法:
擴展方法的語法很簡單,其實就是定義一個函數,只是函數名不要寫成簡單的函數采呐,而是要在函數名前添加被擴展的類(或接口)名和點號(.)。例如:
如上:程序為Raw類擴展了info()方法之后搁骑,就像為Raw類增加了info()方法一樣斧吐,所有的Raw對象都可調用info()方法又固。不僅如此,Raw類的子類的實例也可調用info方法煤率。
當然仰冠,擴展也可以為Kotlin系統提供類的增加方法。例如:
切記:用為指定了Int的泛型蝶糯,所以只能對該泛型為Int的list集合可以調用洋只。如果想對所有類型的泛型可以調用的話,可以不指定明確的類型裳涛。(具體泛型的使用會在接下來的泛型篇章里細講)
實際上木张,Kotlin的擴展并沒有真正地修改所有的擴展的類,被擴展的類還是原來的類端三,沒有任何改變舷礼。Kotlin擴展的本質就是定義了一個函數,當程序用對象調用擴展方法時郊闯,Kotlin在編譯時會執(zhí)行靜態(tài)解析--就是根據調用對象妻献,方法名找到擴展函數,轉換為函數調用团赁。
介紹一下子類和父類同時有一個擴展名一樣擴展育拨,調用的時候是如何執(zhí)行的,如下:
如果getName是重寫的方法欢摄,執(zhí)行返回的一定是son熬丧,但是因為擴展執(zhí)行的是靜態(tài)解析(有編譯時候的類型決定,編譯的類型是Base),所以調用的是Base類的getName方法怀挠,返回的是base析蝴。?
此外還有一點需要注意的是,成員的方法的優(yōu)先級高于擴展的方法绿淋,所有如果成員方法和擴展方法具有同一個名字的時候闷畸,那么系統總是會執(zhí)行成員方法,而不會執(zhí)行擴展方法吞滞。(當然參數不同的話佑菩,就無所謂了)
也可以為可空類型擴展方法:如下:
Any這個關鍵字我忘記了在前面介紹沒有,所在還是提上一句裁赠。 Any就是java的object殿漠,在kotlin是一切類型的父類。
2.擴展屬性
Kotlin也允許擴展屬性佩捞,但由于Kotlin的擴展并不是真正修改目標類凸舵,因此Koltin擴展的屬性其實是通過get和set方法實現的,沒有幕后字段失尖。簡單的來說啊奄,擴展的屬性只能是計算屬性!
kotlin的擴展屬性的如下要求:
1.擴展的屬性不能有初始化值(因為沒有存儲屬性值的幕后字段)
2.不能用field關鍵字顯示幕后字段
3.擴展只讀屬性必須提供getter方法掀潮,擴展讀寫屬性必須提供getter和setter方法菇夸。
匿名擴展函數:與原來的區(qū)別在于. + 方法名,將方法名移動到前面:如下:
對于擴展在舉一個最后的例子仪吧。如下:
3.const 宏的介紹
Kotlin與java的一個重大區(qū)別是:Kotlin的final修飾符不能修飾局部變量庄新,因此open自然也不能修飾局部變量。所以Kotlin提供了宏的概念薯鼠,Kotlin提供了const用來修飾可執(zhí)行“宏替換”的常量择诈。定于如下:
const的使用規(guī)則如下:
1.位于頂層或者是對象表達式的成員
2.初始化為基本類型值或者字符串類型
3.沒有自定義的getter方法。
定義宏變量的時候需要注意的是:
MAX_VALUE_ONE這樣的寫法可以出皇,能夠得到確切的值羞芍,但是MAX_VALUE_TWO這樣的寫法是錯誤的,在編譯的時候無法得到確切的值郊艘。
4.final和open
kotlin 有一個非常特別的設計:它會為非抽象類自動添加final修飾符荷科,也會為非抽象方法,非抽象屬性等無須重寫的成員自動添加final修飾符纱注。如果開發(fā)者希望取消kotlin自動添加final修飾符畏浆,可以使用open修飾符,open修飾符與final是反義詞狞贱。(對于這樣的設計刻获,我個人是比較青睞的。因為一個類加上final和不加上final對于性能來說其實差距還是蠻大的瞎嬉,不知道原因的同學可以查閱一下蝎毡。但是對于好多開發(fā)者來說,不會注意這個類加不加final佑颇,即使這個類不需要繼承顶掉。所以說kotlin其實對java和java開發(fā)者來說,真的就是一種優(yōu)化代碼而已挑胸,方便開發(fā)高性能的代碼)痒筒。
二.類的種類:
1.不可變類:不可變類的意思是創(chuàng)建該類的實例后,該實例的屬性值是不可以改變的茬贵。其實很簡單簿透,就是對于參數加上val修飾。如下:
與可變類相比解藻,不可變類的實例在整個生命周期中永遠處于初始化狀態(tài)老充,他的屬性值不可以改變。
下面有這樣一個例子:
通過這樣仍然可以對address里邊的屬性進行改變螟左,在語義上這樣是不對的啡浊,那么如何實現徹徹底底的不可變類呢觅够。如下:
這樣寫就能徹徹底底實現不可變類啦~
密封類:密封類是一種特殊的抽象類,專門用于派生子類巷嚣。密封類與普通類的區(qū)別在于:密封類的子類是固定喘先。密封類的子類必須與密封類本身在同一個文件中,在其他文件中則不能為密封類派生子類廷粒,這樣就限制了在其他文件中派生子類窘拯。
上邊定義了一個密封類,接下來即可在該密封類中定義抽象方法坝茎,由此可見涤姊,密封類的本質就是抽象類。有規(guī)則可以知道嗤放,密封類的所有構造器都必須是private的思喊,不管開發(fā)者是否使用private修飾,系統都會為之自動添加private修飾斤吐。
有人會問到:那些密封類的好處是什么呢搔涝?因為密封類的子類是固定的,編譯器可以清楚地知道密封類有多少個子類和措,所以在使用when表達式的時候庄呈,編譯器可以知道是否覆蓋了所有情況,從而判斷是否需要添加else子句派阱。
嵌套類和內部類:
Kotlin的嵌套類相當于java的靜態(tài)內部類诬留。而kotlin的內部類相當于java的非靜態(tài)內部類。(在kotlin使用內部類的時候贫母,用到了inner這個修飾符 ? inner class className {})
匿名內部類:
Java有一個非常實用的功能:匿名內部類文兑,Kotlin則徹底拋棄了這個功能。不過同學不必擔心腺劣,Kotlin提供了一個更加強大的語法:對象表達式绿贞。
對象表達式和對象聲明:
對象表達式還有如下規(guī)則:
1.對象表達式不能是抽象類,因為系統在創(chuàng)建對象表達式時會立即創(chuàng)建對象橘原。因此不允許將對象表達式定義抽象類籍铁。
2.對象表達式不能定義構造器。但對象表達式可以定義初始化塊趾断,可以通過初始化塊來完成構造器需要完成的事情拒名。
3.對象表達式可以包含內部類(有inner修飾的內部類),不能包含嵌套類芋酌。
指定零個父類型的對象表達式:
當然也可以指定多個父類型的對象表達式:
對象表達式用來代替java的匿名內部類的增显。那么對象聲明就可以實現單例啦~什么是對象聲明呢?如下:
這個就是對象聲明脐帝,和對象表達式很相近同云。他們之前的不同在于對象聲明不能定義在函數或者方法里邊糖权。
伴生對象:
在談到對象聲明的時候,還不得不說另外一個概念就是伴生對象:在類中定義的對象聲明梢杭,可以用companion修飾温兼,這樣該對象就變成了伴生對象。每個類最多只能定義一個伴生對象武契,伴生對象相當于外部類的對象,程序可通過外部類直接訪問:那么我們就來實現kotlin的單例吧~如下:
by修飾符這個是kotlin的委托荡含,我會在接下來篇章里詳細介紹咒唆。companion object {} 就完完整整的實現了單例。因為kotlin不在用static修飾符了释液。全释。那么可以用伴生對象來定義靜態(tài)的常量。
如果細讀的同學可以會問:你剛剛說的是伴生的修飾符 + 對象聲明來實現的那么應該怎么寫吧误债,怎么少了名字呢浸船?
其實名字完全可以省略的,因為在調用的過程中寝蹈,是當前的類名.instance李命, 調用變量的話是類名.DB_NAME等等,所以起不起名字都沒有關系箫老。
伴生對象擴展:
這樣就可以實現對伴生對象擴展啦~~
三.接口:
接口定義的方法既可是抽象方法封字,也可是非抽象的方法。如果一個只讀的屬性沒有定義getter方法耍鬓,kotlin會自動為該屬性添加abstract修飾符阔籽,如果一個讀寫屬性沒有定義getter和setter方法,kotlin會自動為該屬性添加abstract修飾符牲蜀。
Kotlin接口和java接口還有一個區(qū)別就是:Java接口中的所有成員都會自動使用public修飾符笆制。如果要添加訪問修飾符,則只能用public涣达;如果不加修飾符在辆,則系統默認是public。對于不需要被實現重寫的成員呢峭判,我們可以定義private或public修飾(在kotlin接口里开缎,成員可以不必須是抽象的。方法可以定義實體)林螃。默認的情況下是public修飾符代替奕删。
因為本篇幅有點過長,還剩下枚舉和類的委托沒有介紹疗认,打算在下一篇介紹完残。下篇近期會更新伏钠。如果感覺還不錯的同學那么點波關注吧~~