一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

重學(xué) Kotlin —— object,史上最 “快” 單例 ?

 univasity 2024-03-19 發(fā)布于法國(guó)

前言

這里是專欄 重學(xué) Kotlin,靈感來自于 Medium 上 Android Developers 團(tuán)隊(duì)的 Kotlin Vocabulary 。

作為一名 Kotlin 老鐵粉,我可能在博客里不止一次的表達(dá)過對(duì) Kotlin 的態(tài)度。

都 2020 了,作為一名安卓開發(fā)者,再不會(huì) Kotlin ,真的說不過去了!

介紹 Kotlin 語(yǔ)法的文章很多,那么,在這個(gè)系列中,我會(huì)寫一些什么呢?

Kotlin 再?gòu)?qiáng)大,也逃脫不了在 JVM 上運(yùn)行。經(jīng)過 kotlinc 編譯之后,生成的依舊是 .class 文件。

所以,學(xué)習(xí) Kotlin 的最佳方式其實(shí)就是查看字節(jié)碼。Android Studio 直接提供了插件,按如下方式即可查看:

Tools -> Kotlin -> Show Kotlin Bytecode

當(dāng)然,字節(jié)碼可讀性太差,IDE 提供了 Decompile ,將字節(jié)碼轉(zhuǎn)換成 Java 代碼。

這樣,我們就可以輕松掌握 Kotlin 各種語(yǔ)法的本質(zhì)。

本系列的每一篇文章都會(huì)選擇一個(gè)關(guān)鍵字或者知識(shí)點(diǎn),剖析本質(zhì),幫助大家快速深入理解 Kotlin 。

下面就進(jìn)入今天的主角 object

目錄

  1. object 有哪些用法?

  2. 對(duì)象聲明 —— 一個(gè)關(guān)鍵字實(shí)現(xiàn)單例 ?

  3. 伴生對(duì)象 —— static 的代替者 ?

  4. 對(duì)象表達(dá)式 —— Kotlin 的匿名內(nèi)部類 ?

  5. 這到底是哪種用法 ?

正文

object 的三種用法

Kotlin 的 object 關(guān)鍵字有三種用法:

  • 對(duì)象聲明 ,一般用來實(shí)現(xiàn)單例
  • 伴生對(duì)象 ,類似 Java 的 static 關(guān)鍵字,也可以用于工廠方法模式
  • 對(duì)象表達(dá)式 ,一般用來代替 Java 的匿名內(nèi)部類

下面就逐個(gè)來看看這三種用法的本質(zhì)。

對(duì)象聲明

object 的語(yǔ)義是這樣的: 定義一個(gè)類并創(chuàng)建一個(gè)實(shí)例 。不管是對(duì)象聲明,還是下面會(huì)說到的另外兩種用法,都是遵循這一語(yǔ)義的。

作為對(duì)象聲明,它可以直接用來實(shí)現(xiàn)單例模式:

復(fù)制代碼
object Singleton{ fun xxx(){} }

話不多說,直接 Decompile 看 Java 代碼:

復(fù)制代碼
public final class Singleton { public static final Singleton INSTANCE; public final void xxx() { } private Singleton() { } static { Singleton var0 = new Singleton(); INSTANCE = var0; } }

從 Java 代碼中可以看出來,顯然這是一個(gè)單例模式。

  • 私有構(gòu)造函數(shù)
  • 通過靜態(tài)字段對(duì)外提供實(shí)例
  • 靜態(tài)代碼塊中直接初始化,線程安全 。

這里插播一個(gè)問題,static 代碼塊在何時(shí)執(zhí)行?

首先類加載階段可以分為加載驗(yàn)證、準(zhǔn)備、解析、初始化使用、卸載 七個(gè)步驟 。static 代碼塊就是在 初始化 階段執(zhí)行的。那么,哪些場(chǎng)景會(huì)觸發(fā)類的初始化呢?有如下幾種場(chǎng)景:

  • 通過 new 實(shí)例化對(duì)象
  • 讀寫一個(gè)類的靜態(tài)字段
  • 調(diào)用一個(gè)類的靜態(tài)方法
  • 對(duì)類進(jìn)行反射調(diào)用

按照上面反編譯出來的 Java 代碼,獲得單例對(duì)象的方法是 Singleton.INSTANCE ,即調(diào)用 Singleon 類的靜態(tài)字段 INSTANCE,就會(huì)觸發(fā)類的初始化階段,也就觸發(fā)了 static 代碼塊的執(zhí)行,從而完成了單例對(duì)象的實(shí)例化。同時(shí),由于類加載過程天生線程安全,所以 Kotlin 的 object 單例活脫脫的就是一個(gè)線程安全的懶漢式單例(訪問時(shí)初始化)。

此外,object 聲明的單例類和普通類一樣,可以實(shí)現(xiàn)接口,繼承類,也可以包含屬性,方法。但是它不能由開發(fā)者手動(dòng)聲明構(gòu)造函數(shù),從反編譯出來的 Java 代碼可以看到,它只有一個(gè) private 構(gòu)造函數(shù)。

所以,這對(duì)實(shí)際的業(yè)務(wù)場(chǎng)景是有一定限制的。對(duì)于需要攜帶參數(shù)的單例類,object 就有點(diǎn)力不從心了。當(dāng)然也不難解決,模仿 Java 的寫法就行了,這里以 DCL 模式為例。

復(fù)制代碼
class Singleton private constructor(private val param: Int) { companion object { @Volatile private var instance: Singleton? = null fun getInstance(property: Int) = instance ?: synchronized(this) { instance ?: Singleton(property).also { instance = it } } } }

說到這,你應(yīng)該了解了 object 實(shí)現(xiàn)單例模式的本質(zhì)。下面來看看 伴生對(duì)象 。

伴生對(duì)象

你可以回想一下,你在 Kotlin 中使用過 static 關(guān)鍵字嗎?答案肯定是沒有。通常我們可以在頂層文件中直接定義常量和頂層函數(shù),但有的時(shí)候我們的確需要在類中定義靜態(tài)常量或函數(shù),這樣顯得更加直觀。這就是 伴生對(duì)象 的應(yīng)用場(chǎng)景。

伴生對(duì)象,顧名思義,就是伴隨著類而存在的對(duì)象,在類加載的時(shí)候初始化。

復(fù)制代碼
class User(val male: Int){ companion object { val MALE = 0 fun isMale(male:Int) = male == MALE } }

這樣就可以像調(diào)用 static 一樣調(diào)用伴生對(duì)象中的屬性和函數(shù),而無(wú)需創(chuàng)造類實(shí)例。

復(fù)制代碼
User.MALE User.isMale(1)

還是直接看 Java 代碼。

復(fù)制代碼
public final class User { private final int male; private static final int MALE = 0; public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null); public final int getMale() { return this.male; } public User(int male) { this.male = male; } public static final class Companion { public final int getMALE() { return User.MALE;public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null); } public final boolean isMale(int male) { return male == ((User.Companion)this).getMALE(); } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } }

編譯器為我們生成了一個(gè)叫做 Companion 的靜態(tài)內(nèi)部類,注意它的 getMale()isMale() 方法并不是靜態(tài)方法,所以實(shí)際去訪問的時(shí)候還是需要一個(gè) Companion 實(shí)例的。這里實(shí)例就是 User 類中定義的靜態(tài)成員變量 Companion

復(fù)制代碼
public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);

看到靜態(tài)字段,又該想到在類加載的時(shí)候初始化的了。那么,哪個(gè)操作觸發(fā)了類加載呢?我們來反編譯一下 User.MALE 的 Java 代碼。

復(fù)制代碼
User.Companion.getMALE();

所以也是訪問時(shí)時(shí)會(huì)初始化伴生對(duì)象。再回想一下前面說過的,

object 其實(shí)我們可以把它理解成 定義一個(gè)類并創(chuàng)建一個(gè)實(shí)例 。

伴生對(duì)象仍舊符合這一語(yǔ)義。

在 Java 中如何調(diào)用伴生對(duì)象呢?User.Companion.isMale(1) 即可。另外,我們可以給伴生對(duì)象命名,如下所示:

復(fù)制代碼
companion object X { ... }

那么,編譯器生成的類就不是 Companion 了,而是 X 。在 Java 中就可以用 User.X.isMale(1) 了。

了解了伴生對(duì)象的本質(zhì)之后,再來說兩個(gè)它的其他用法。

創(chuàng)建靜態(tài)工廠方法

復(fù)制代碼
interface Car { val brand: String companion object { operator fun invoke(type: CarType): Car { return when (type) { CarType.AUDI -> Audi() CarType.BMW -> BMW() } } } }

這里重載了 invoke() 方法,調(diào)用時(shí)直接 Car(CarType.BMW) 即可。你可以試著用 Java 代碼實(shí)現(xiàn)上面的邏輯,對(duì)比一下。

伴生對(duì)象擴(kuò)展方法

伴生對(duì)象也是支持?jǐn)U展方法的。還是以上面的 Car 為例,定義一個(gè)根據(jù)汽車品牌獲取汽車類型的擴(kuò)展方法。

復(fù)制代碼
fun Car.Companion.getCarType(brand:String) :CarType { ...... }

雖然是在 Car.Companion 上定義的擴(kuò)展函數(shù),但實(shí)際上相當(dāng)于給 Car 增加了一個(gè)靜態(tài)方法,使用方式如下:

復(fù)制代碼
Car.getCarType("BMW")

對(duì)象表達(dá)式

對(duì)象表達(dá)式最經(jīng)典的用法就是用來 代替 Java 的匿名內(nèi)部類 。例如常見的點(diǎn)擊事件:

復(fù)制代碼
xxx.setOnClickListener(object : View.OnClickListener{ override fun onClick(v: View) { } })

這和 Java 的匿名內(nèi)部類是等價(jià)的。只不過像上面的單方法接口,我們很少用 object 寫,而是用 lambda 代替,顯得更加簡(jiǎn)潔。

復(fù)制代碼
xxx.setOnClickListener { view -> ...... }

當(dāng)匿名對(duì)象需要重寫多個(gè)方法時(shí),就只能選擇對(duì)象表達(dá)式了。

和 Java 的匿名內(nèi)部類一樣,對(duì)象聲明中也可以訪問外部變量。

對(duì)象表達(dá)式應(yīng)該是 object 最樸實(shí)無(wú)華的使用方式了。

最后

看到這里,你應(yīng)該已經(jīng)完全掌握了 object 關(guān)鍵字的本質(zhì)。那么,我也要來考考你,仔細(xì)看下面的代碼:

復(fù)制代碼
class MainActivity : AppCompatActivity() { val a = 1 object click : View.OnClickListener { override fun onClick(v: View) { val b = a + 1 } } }
  • 上面的代碼可以正確編譯嗎?為什么?
  • 這里 object 的用法屬于哪一種?史上快

    重學(xué) Kotlin —— object,史上最 “快” 單例 ?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    老鸭窝精彩从这里蔓延| 国产成人精品久久二区二区| 精品少妇人妻av一区二区蜜桃 | 在线免费国产一区二区| 国产色偷丝袜麻豆亚洲| 国产又粗又深又猛又爽又黄| 亚洲熟女乱色一区二区三区| 中文字幕中文字幕在线十八区| 国产综合一区二区三区av| 麻豆蜜桃星空传媒在线观看| 国产又色又爽又黄又大| 婷婷色网视频在线播放| 黄片免费播放一区二区| 亚洲欧美日本国产不卡 | 欧美午夜不卡在线观看| 欧美日韩国产精品自在自线| 久久国产精品热爱视频| 中文字字幕在线中文乱码二区| 日韩中文字幕欧美亚洲| 激情中文字幕在线观看| 久久碰国产一区二区三区| 国产精品一区二区视频| 国产亚洲系列91精品| 日韩黄色一级片免费收看| 手机在线不卡国产视频| 99久久国产亚洲综合精品| 亚洲一区二区三区熟女少妇| 久久精品福利在线观看| 麻豆视传媒短视频免费观看| 国产又粗又长又爽又猛的视频| 手机在线观看亚洲中文字幕| 色一情一乱一区二区三区码| 人体偷拍一区二区三区| 亚洲中文字幕高清视频在线观看| 91香蕉视频精品在线看| 日本高清不卡一二三区| 爱草草在线观看免费视频| 久久精品伊人一区二区| 91精品国产综合久久福利| 国产日产欧美精品大秀| 大胆裸体写真一区二区|