Kotlin中的5種單例模式示例詳解
前言
最近在學(xué)習(xí)Kotlin這門語(yǔ)言,在項(xiàng)目開(kāi)發(fā)中,運(yùn)用到了單例模式。因?yàn)槠浔磉_(dá)方式與Java是不同的。所以對(duì)不同單例模式的實(shí)現(xiàn)進(jìn)行了分別探討。主要單例模式實(shí)現(xiàn)如下:
- 餓漢式
- 懶漢式
- 線程安全的懶漢式
- 雙重校驗(yàn)鎖式
- 靜態(tài)內(nèi)部類式
PS:該篇文章不討論單例模式的運(yùn)用場(chǎng)景與各種模式下的單例模式的優(yōu)缺點(diǎn)。只討論在Java下不同單例模式下的對(duì)應(yīng)Kotlin實(shí)現(xiàn)。
一、餓漢式實(shí)現(xiàn)
//Java實(shí)現(xiàn)
public class SingletonDemo {
private static SingletonDemo instance=new SingletonDemo();
private SingletonDemo(){
}
public static SingletonDemo getInstance(){
return instance;
}
}
//Kotlin實(shí)現(xiàn)
object SingletonDemo
這里很多小伙伴,就吃了一驚。我靠一個(gè)object 關(guān)鍵字就完成相同的功能?一行代碼?
Kotlin的對(duì)象聲明
學(xué)習(xí)了Kotlin的小伙伴肯定知道,在Kotlin中類沒(méi)有靜態(tài)方法。如果你需要寫一個(gè)可以無(wú)需用一個(gè)類的實(shí)例來(lái)調(diào)用,但需要訪問(wèn)類內(nèi)部的函數(shù)(例如,工廠方法,單例等),你可以把該類聲明為一個(gè)對(duì)象。該對(duì)象與其他語(yǔ)言的靜態(tài)成員是類似的。如果你想了解Kotlin對(duì)象聲明的更多內(nèi)容。請(qǐng)點(diǎn)擊- - - 傳送門
到這里,如果還是有很多小伙伴不是很相信一行代碼就能解決這個(gè)功能,我們可以通過(guò)一下方式查看Kotlin的字節(jié)碼。
查看Kotlin對(duì)應(yīng)字節(jié)碼

我們進(jìn)入我們的Android Studio(我的Android Studio 3.0,如果你的編譯器版本過(guò)低,請(qǐng)自動(dòng)升級(jí)) 選擇Tools工具欄,選擇"Kotlin",選擇“Show Kotlin Bytecode"
選擇過(guò)后就會(huì)進(jìn)入到下方界面:

點(diǎn)擊"Decompile" 根據(jù)字節(jié)碼得到以下代碼
public final class SingletonDemo {
public static final SingletonDemo INSTANCE;
private SingletonDemo(){}
static {
SingletonDemo var0 = new SingletonDemo();
INSTANCE = var0;
}
}
通過(guò)以上代碼,我們了解事實(shí)就是這個(gè)樣子的,使用Kotlin"object"進(jìn)行對(duì)象聲明與我們的餓漢式單例的代碼是相同的。
二、懶漢式
//Java實(shí)現(xiàn)
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
//Kotlin實(shí)現(xiàn)
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
fun get(): SingletonDemo{
//細(xì)心的小伙伴肯定發(fā)現(xiàn)了,這里不用getInstance作為為方法名,是因?yàn)樵诎樯鷮?duì)象聲明時(shí),內(nèi)部已有g(shù)etInstance方法,所以只能取其他名字
return instance!!
}
}
}
上述代碼中,我們可以發(fā)現(xiàn)在Kotlin實(shí)現(xiàn)中,我們讓其主構(gòu)造函數(shù)私有化并自定義了其屬性訪問(wèn)器,其余內(nèi)容大同小異。
如果有小伙伴不清楚Kotlin構(gòu)造函數(shù)的使用方式。請(qǐng)點(diǎn)擊 - - - 構(gòu)造函數(shù)
不清楚Kotlin的屬性與訪問(wèn)器,請(qǐng)點(diǎn)擊 - - -屬性和字段
三、線程安全的懶漢式
//Java實(shí)現(xiàn)
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){}
public static synchronized SingletonDemo getInstance(){//使用同步鎖
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
//Kotlin實(shí)現(xiàn)
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
@Synchronized
fun get(): SingletonDemo{
return instance!!
}
}
}
大家都知道在使用懶漢式會(huì)出現(xiàn)線程安全的問(wèn)題,需要使用使用同步鎖,在Kotlin中,如果你需要將方法聲明為同步,需要添加**@Synchronized**注解。
四、雙重校驗(yàn)鎖式(Double Check)
//Java實(shí)現(xiàn)
public class SingletonDemo {
private volatile static SingletonDemo instance;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
}
//kotlin實(shí)現(xiàn)
class SingletonDemo private constructor() {
companion object {
val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonDemo() }
}
}
哇!小伙伴們驚喜不,感不感動(dòng)啊。我們居然幾行代碼就實(shí)現(xiàn)了多行的Java代碼。其中我們運(yùn)用到了Kotlin的延遲屬性 Lazy。
Lazy是接受一個(gè) lambda 并返回一個(gè) Lazy 實(shí)例的函數(shù),返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托: 第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給 lazy() 的 lambda 表達(dá)式并記錄結(jié)果, 后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。
這里還有有兩個(gè)額外的知識(shí)點(diǎn)。
如果你了解以上知識(shí)點(diǎn),我們直接來(lái)看Lazy的內(nèi)部實(shí)現(xiàn)。
Lazy內(nèi)部實(shí)現(xiàn)
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
觀察上述代碼,因?yàn)槲覀儌魅氲膍ode = LazyThreadSafetyMode.SYNCHRONIZED,
那么會(huì)直接走 SynchronizedLazyImpl,我們繼續(xù)觀察SynchronizedLazyImpl。
Lazy接口
SynchronizedLazyImpl實(shí)現(xiàn)了Lazy接口,Lazy具體接口如下:
public interface Lazy<out T> {
//當(dāng)前實(shí)例化對(duì)象,一旦實(shí)例化后,該對(duì)象不會(huì)再改變
public val value: T
//返回true表示,已經(jīng)延遲實(shí)例化過(guò)了,false 表示,沒(méi)有被實(shí)例化,
//一旦方法返回true,該方法會(huì)一直返回true,且不會(huì)再繼續(xù)實(shí)例化
public fun isInitialized(): Boolean
}
繼續(xù)查看SynchronizedLazyImpl,具體實(shí)現(xiàn)如下:
SynchronizedLazyImpl內(nèi)部實(shí)現(xiàn)
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
//判斷是否已經(jīng)初始化過(guò),如果初始化過(guò)直接返回,不在調(diào)用高級(jí)函數(shù)內(nèi)部邏輯
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
}
else {
val typedValue = initializer!!()//調(diào)用高級(jí)函數(shù)獲取其返回值
_value = typedValue //將返回值賦值給_value,用于下次判斷時(shí),直接返回高級(jí)函數(shù)的返回值
initializer = null
typedValue
}
}
}
//省略部分代碼
}
通過(guò)上述代碼,我們發(fā)現(xiàn) SynchronizedLazyImpl 覆蓋了Lazy接口的value屬性,并且重新了其屬性訪問(wèn)器。其具體邏輯與Java的雙重檢驗(yàn)是類似的。
到里這里其實(shí)大家還是肯定有疑問(wèn),我這里只是實(shí)例化了SynchronizedLazyImpl對(duì)象,并沒(méi)有進(jìn)行值的獲取,它是怎么拿到高階函數(shù)的返回值呢?。這里又涉及到了委托屬性。
委托屬性語(yǔ)法是: val/var <屬性名>: <類型> by <表達(dá)式>。在 by 后面的表達(dá)式是該 委托, 因?yàn)閷傩詫?duì)應(yīng)的 get()(和 set())會(huì)被委托給它的 getValue() 和 setValue() 方法。 屬性的委托不必實(shí)現(xiàn)任何的接口,但是需要提供一個(gè) getValue() 函數(shù)(和 setValue()——對(duì)于 var 屬性)。
而Lazy.kt文件中,聲明了Lazy接口的getValue擴(kuò)展函數(shù)。故在最終賦值的時(shí)候會(huì)調(diào)用該方法。
@kotlin.internal.InlineOnly //返回初始化的值。 public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
五、靜態(tài)內(nèi)部類式
//Java實(shí)現(xiàn)
public class SingletonDemo {
private static class SingletonHolder{
private static SingletonDemo instance=new SingletonDemo();
}
private SingletonDemo(){
System.out.println("Singleton has loaded");
}
public static SingletonDemo getInstance(){
return SingletonHolder.instance;
}
}
//kotlin實(shí)現(xiàn)
class SingletonDemo private constructor() {
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder= SingletonDemo()
}
}
靜態(tài)內(nèi)部類的實(shí)現(xiàn)方式,也沒(méi)有什么好說(shuō)的。Kotlin與Java實(shí)現(xiàn)基本雷同。
最后
附上我寫的一個(gè)基于Kotlin 仿開(kāi)眼的項(xiàng)目SimpleEyes(ps: 其實(shí)在我之前,已經(jīng)有很多小朋友開(kāi)始仿這款應(yīng)用了,但是我覺(jué)得要做就做好。所以我的項(xiàng)目和其他的人應(yīng)該不同,不僅僅是簡(jiǎn)單的一個(gè)應(yīng)用。但是,但是。但是。重要的話說(shuō)三遍。還在開(kāi)發(fā)階段,不要打我),歡迎大家follow和start
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android自定義View實(shí)現(xiàn)跟隨手指移動(dòng)的小兔子
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)跟隨手指移動(dòng)的小兔子,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
詳解Android Studio實(shí)現(xiàn)用戶登陸界面demo(xml實(shí)現(xiàn))
這篇文章主要介紹了詳解Android Studio實(shí)現(xiàn)用戶登陸界面demo,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
Android填坑系列:在小米系列等機(jī)型上放開(kāi)定位權(quán)限后的定位請(qǐng)求彈框示例
本文詳細(xì)介紹了在小米系列等機(jī)型上放開(kāi)定位權(quán)限后的定位請(qǐng)求彈框示例,例如在應(yīng)用軟件中提示顯示定位服務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11
Android開(kāi)發(fā)實(shí)現(xiàn)的幾何圖形工具類GeometryUtil完整實(shí)例
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)的幾何圖形工具類GeometryUtil,涉及Android坐標(biāo)圖形數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
Android逆向之dex2oat的實(shí)現(xiàn)解析
虛擬機(jī)的發(fā)生展經(jīng)歷了從初期的dalvik,到中期的dalvik,以及后期的ART。但是市面上的APK文件早已已經(jīng)全球流行。為了能夠讓這些APK不加改動(dòng)的在所有虛擬機(jī)上面運(yùn)行,google采用了類似適配器模式。即在讓虛擬運(yùn)行之前多一道工序。就是dexopt2021-10-10
詳解Android應(yīng)用中使用TabHost組件進(jìn)行布局的基本方法
這篇文章主要介紹了Android應(yīng)用中使用TabHost組件進(jìn)行布局的基本方法,不繼承TabActivity并以最基本的布局文件方式進(jìn)行布局,需要的朋友可以參考下2016-04-04
Android Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的方法示例
本篇文章主要介紹了Android Intent實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
Android ExpandableListView用法示例詳解
ExpandableListView 是 Android 中一個(gè)非常實(shí)用的列表控件,它可以幫助我們實(shí)現(xiàn)具有分組功能的列表展示,通過(guò)本文的介紹,你應(yīng)該已經(jīng)掌握了 ExpandableListView 的基本用法,感興趣的朋友跟隨小編一起看看吧2025-02-02
Android?懸浮按鈕之實(shí)現(xiàn)兔兔按鈕示例
這篇文章主要為大家介紹了Android?懸浮按鈕之實(shí)現(xiàn)兔兔按鈕示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

