自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

漫畫(huà):Kotlin 的擴(kuò)展細(xì)節(jié)探究

企業(yè)動(dòng)態(tài)
擴(kuò)展并不是 Kotlin 首創(chuàng)的,在 C# 和 Gosu 里,其實(shí)早就有類(lèi)似的實(shí)現(xiàn),Kotlin 本身在設(shè)計(jì)之初,就參考了很多語(yǔ)言的優(yōu)點(diǎn)!

 [[229394]]

擴(kuò)展

擴(kuò)展并不是 Kotlin 首創(chuàng)的,在 C# 和 Gosu 里,其實(shí)早就有類(lèi)似的實(shí)現(xiàn),Kotlin 本身在設(shè)計(jì)之初,就參考了很多語(yǔ)言的優(yōu)點(diǎn)!

Kotlin 可以利用擴(kuò)展,在不繼承父類(lèi)也不使用任何裝飾器設(shè)計(jì)模式的情況下,對(duì)指定的類(lèi)進(jìn)行功能的擴(kuò)展。

Kotlin 的擴(kuò)展包含了擴(kuò)展函數(shù)和擴(kuò)展屬性,需要適用特殊的聲明方式來(lái)完成。也就是說(shuō)你可以對(duì)任何類(lèi),增加一些方法或者屬性,來(lái)增強(qiáng)它的功能。

比較常見(jiàn)的場(chǎng)景,就是原本我們需要實(shí)現(xiàn)的各種 SpUtils、ViewUtils 之類(lèi)的各種 XxxUtils 工具類(lèi)。如果需要,我們可以直接在對(duì)應(yīng)的類(lèi)上,進(jìn)行直接擴(kuò)展。

說(shuō)的這么厲害,舉個(gè)實(shí)際的例子就可以說(shuō)明一切了。我一般會(huì)在項(xiàng)目?jī)?nèi)建立一個(gè) SpUtils 的幫助類(lèi),來(lái)幫我們快速的操作 SharePreferences。

  1. fun Context.getSpString(key:String):String{ 
  2.     val sp = getSharedPreferences("cxmy_sp",Context.MODE_PRIVATE) 
  3.     return sp.getString(key,""

在這個(gè)例子中,我們對(duì) Context 類(lèi)進(jìn)行擴(kuò)展,為了讓它能夠支持快速的從 SharePreferences 中獲取到持久化的數(shù)據(jù)。當(dāng)然,我們還是要傳遞進(jìn)去一個(gè)我們存儲(chǔ)數(shù)據(jù)的 Key。

這樣使用它就非常的簡(jiǎn)單了,我們可以直接能夠持有 Context 的地方,直接調(diào)用 getSpString() 方法。

  1. // Activity 中 
  2. getSpString("cxmy"
  3. // or 
  4. mContext.getSpString("cxmy"

擴(kuò)展是靜態(tài)解析的

我們知道,Kotlin 最終依然會(huì)被編譯成 Java 的字節(jié)碼在虛擬機(jī)中運(yùn)行。Kotlin 也無(wú)法突破 Java 中不被允許的操作限制,所以它并不能真正的修改他們所擴(kuò)展的類(lèi)。

通過(guò)定義一個(gè)擴(kuò)展,其實(shí)你并沒(méi)有在一個(gè)現(xiàn)有類(lèi)中,真的插入一個(gè)新的方法或者屬性,僅僅是可以通過(guò)該類(lèi)型的變量,用點(diǎn)表達(dá)式調(diào)用這個(gè)新方法或者屬性。

類(lèi)是允許繼承的,而靜態(tài)解析這一規(guī)則,就是為了在類(lèi)的繼承這一點(diǎn)上,不存在二義性。

當(dāng)父類(lèi)以及它的子類(lèi),都通過(guò)擴(kuò)展的方式,增加一個(gè) foo() 方法的時(shí)候,具體在調(diào)用的時(shí)候,是調(diào)用父類(lèi)的 foo() 方法還是子類(lèi)的 foo() 方法,完全取決于調(diào)用時(shí),表達(dá)式所在的類(lèi)型決定的,而不是由表達(dá)式運(yùn)行時(shí)的類(lèi)型決定的。

這里強(qiáng)調(diào)的擴(kuò)展是靜態(tài)解析的,即他們不是根據(jù)接受者類(lèi)型的虛方法來(lái)判定調(diào)用那個(gè)方法。

一例勝千文,我們依然來(lái)舉個(gè)例子。

  1. open class A() 
  2. class B:A(){ 
  3. fun A.foo(){ 
  4.     Log.i("cxmy","A.foo"
  5. fun B.foo(){ 
  6.     Log.i("cxmy","B.foo"
  7. fun printFoo(a: A){ 
  8.     a.foo() 
  9. printFoo(B()) 

在這個(gè)例子中,我們傳遞進(jìn)去的是 B 對(duì)象,但是實(shí)際上會(huì)調(diào)用 A.foo() 方法,所以輸出應(yīng)該是 "A.foo()"。

這也印證了擴(kuò)展是依據(jù)調(diào)用所在的表達(dá)式類(lèi)型來(lái)決定的,而不是由表達(dá)式運(yùn)行時(shí)的類(lèi)型決定的。

在 Kotlin 中,使用 is 操作符,會(huì)讓代碼塊中的類(lèi)型有一次隱式轉(zhuǎn)換,但是它對(duì)擴(kuò)展是無(wú)效的,如果有特殊要求,可以使用 as 操作符顯式的進(jìn)行強(qiáng)轉(zhuǎn),方可生效。

  1. fun foo(){ 
  2.     val b = B() 
  3.     b.foo() 
  4.     if(b is A){ 
  5.       (b as A).foo() 
  6.       b.foo() 
  7.     } 

隨手運(yùn)行一下,它的結(jié)果就明朗了。

  1. B.foo() 
  2. A.foo() 
  3. B.foo() 

不過(guò)雖說(shuō)靜態(tài)解析這一規(guī)則是為了限制繼承的歧義,但是正常使用擴(kuò)展,它其實(shí)是可以在其繼承者身上調(diào)用的。例如在 Context 類(lèi)上擴(kuò)展了某個(gè)方法,同樣可以通過(guò) Activity 或者 Server 這些 Context 的子類(lèi)進(jìn)行調(diào)用,它們并不沖突。

可空接收者

擴(kuò)展的類(lèi)的類(lèi)型,也可以是一個(gè)可空的接收者類(lèi)型。也就是我們可以在一個(gè)可空的類(lèi)上定義擴(kuò)展,大大的增加了擴(kuò)展的適用范圍。

  1. fun Any?.toString(): String { 
  2.     if (this == nullreturn "null" 
  3.     // 空檢測(cè)之后,“this”會(huì)自動(dòng)轉(zhuǎn)換為非空類(lèi)型,所以下面的 toString() 
  4.     // 解析為 Any 類(lèi)的成員函數(shù) 
  5.     return toString() 

在這個(gè)例子中,我們?cè)谌我鈱?duì)象上,通過(guò)擴(kuò)展實(shí)現(xiàn)了 toString() 方法,注意這里擴(kuò)展的類(lèi)是 Any? ,它是允許在一個(gè)為 null 的對(duì)象上直接調(diào)用的。

擴(kuò)展屬性

與函數(shù)類(lèi)似,Kotlin 同樣支持?jǐn)U展屬性。

  1. val Context.pgName: String 
  2.     get() = "com.cxmy.dev" 

和擴(kuò)展方法一樣,擴(kuò)展屬性不過(guò)擴(kuò)展屬性并不等于這個(gè)類(lèi)上真實(shí)的屬性,它并沒(méi)有實(shí)際的將這個(gè)屬性插入到這個(gè)類(lèi)當(dāng)中。

因此,對(duì)擴(kuò)展屬性來(lái)說(shuō),幕后字段 field 是不存在的,所以我們沒(méi)法寫(xiě)類(lèi)似這樣的代碼,并且擴(kuò)展屬性不能有初始化器。

  1. var stringRepresentation: String = "cxmyDev" 
  2.     get() = field.toString() 
  3.     set(value) { 
  4.         field = value // 解析字符串并賦值給其他屬性 
  5.     } 

雖然擴(kuò)展屬性沒(méi)有幕后字段,但是它們的行為我們依然可以通過(guò)顯示提供的 getters/setters 來(lái)定義。

例如:

  1. var Context.channel: String 
  2.     get() { 
  3.         return getSpString("channel"
  4.     } 
  5.     set(value) { 
  6.         setSpString("channel",value) 
  7.     } 

雖然沒(méi)有幕后字段 field ,但是我們可以將值存儲(chǔ)在其他地方,這里舉例將其存儲(chǔ)在 SharePreferences 里。

在 Java 中調(diào)用 Kotlin 的擴(kuò)展代碼

首先,Kotlin 在設(shè)計(jì)之初,就已經(jīng)考慮了和 Java 互相調(diào)用的問(wèn)題,所以這一點(diǎn)我們完全不用擔(dān)心,不知道怎么調(diào)用,只要去找對(duì)應(yīng)的調(diào)用方法就好了。

例如文檔中的例子:

在 org.foo.bar 包內(nèi)的 example.kt 文件中聲明的所有函數(shù)和屬性,包括擴(kuò)展函數(shù),都會(huì)編譯成一個(gè)名為 org.foo.bar.ExampleKt 的 Java 類(lèi)的靜態(tài)方法。

這也印證了前面提到的,對(duì)于 Kotlin 的擴(kuò)展,它并不會(huì)真的在擴(kuò)展類(lèi)中,插入一個(gè)方法或者屬性,而是以一個(gè) XxxKt 的命名方式命名的類(lèi)的形似存在。

而 Kotlin 的擴(kuò)展,在轉(zhuǎn)換為 Java 字節(jié)碼的時(shí)候,會(huì)進(jìn)行特殊處理,會(huì)自動(dòng)生成另外一個(gè)方法簽名。

例如:

  1. // SpUtils.kt 
  2. fun Context.getSpString(key: String): String { 
  3.     val sp = getSharedPreferences("cxmy_sp", Context.MODE_PRIVATE) 
  4.     return sp.getString(key""

會(huì)變成:

  1. // SpUtilsKt.java 
  2. public static final String getSpString(Context context,String key){ 
  3.   //... 

可以看到它幫我們生成的方法中,會(huì)將擴(kuò)展依賴的類(lèi)當(dāng)成一個(gè)參數(shù)傳遞給這個(gè)靜態(tài)方法。這樣,我們?cè)?Java 中的調(diào)用,就清晰了。

  1. SpUtilsKt.getSpString(context,"channel"

擴(kuò)展屬性也一樣,會(huì)變成一個(gè) getXxx() 的方法,就不再贅述了。

雖然 XxxKt 這個(gè)類(lèi)是自動(dòng)生成的,我們無(wú)需關(guān)心細(xì)節(jié)。如果對(duì)這個(gè)命名有特殊嗜好,其實(shí)可以通過(guò) @JvmName 注釋?zhuān)薷纳傻?Java 類(lèi)的類(lèi)名,需要注意的是 @JvmName 注釋?zhuān)枰虞d kt 文件的首行,前面不能有其他代碼。

  1. @file:JvmName("SpHelper"
  2.  
  3. // ... 

這樣,我們?cè)?Java 代碼中調(diào)用的時(shí)候,就脫離了 Kt 字段,更像是一個(gè)原本就用 Java 語(yǔ)言編寫(xiě)的方法了。

擴(kuò)展到這里就完全清晰了,有的點(diǎn)都涉及到了。實(shí)際上 Google I/O 上發(fā)布的 AndroidX KTX,基本上就是依賴 Kotlin 的擴(kuò)展功能實(shí)現(xiàn)的,還不了解 Android KTX 的可以戳這里。

擴(kuò)展對(duì)于 Kotlin 的意義非凡,也確實(shí)能讓我們編寫(xiě)的代碼更清晰以及調(diào)用起來(lái)更方便。

 

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2015-05-18 09:50:23

swift擴(kuò)展

2024-03-20 08:31:40

KotlinExtension計(jì)算

2025-01-02 15:47:21

2025-04-16 08:20:50

Kotlin函數(shù)代碼

2013-06-04 15:41:31

iOS開(kāi)發(fā)移動(dòng)開(kāi)發(fā)block

2010-08-02 16:51:54

2017-03-06 16:13:41

深度學(xué)習(xí)人工智能

2013-07-23 10:26:51

移動(dòng)設(shè)備漫畫(huà)轉(zhuǎn)型

2009-06-29 15:18:00

JavaFX綁定

2023-11-17 08:02:34

系統(tǒng)調(diào)用linux

2017-08-09 08:56:04

SP存儲(chǔ)Android

2010-05-25 13:22:43

2011-08-18 14:05:12

NoSQL

2010-01-19 17:28:36

東方通中間件

2013-08-07 09:23:28

云技術(shù)公有云虛擬化平臺(tái)

2014-04-02 17:10:00

虛擬應(yīng)用工作原理

2015-07-23 10:20:30

混合云云計(jì)算IT基礎(chǔ)架構(gòu)

2012-09-13 09:35:35

CSSJS編程

2014-11-07 13:48:20

云計(jì)算

2014-11-10 09:29:13

Google
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)