欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

kotlin object關(guān)鍵字單例模式實現(xiàn)示例詳解

 更新時間:2023年01月12日 09:46:20   作者:TimeFine  
這篇文章主要為大家介紹了kotlin object關(guān)鍵字單例模式實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

object 關(guān)鍵字有三種不同的語義:匿名內(nèi)部類、伴生對象、單例模式。因為 Kotlin 的設(shè)計者認(rèn)為,這三種語義本質(zhì)上都是在定義一個類的同時還創(chuàng)建了對象。在這樣的情況下,與其分別定義三種不同的關(guān)鍵字,還不如將它們統(tǒng)一成 object 關(guān)鍵字。

一、 匿名內(nèi)部類

Android中用java寫View的點擊事件:

findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //do something
    }
});

在 Kotlin 當(dāng)中,我們會使用 object 關(guān)鍵字來創(chuàng)建匿名內(nèi)部類。同樣,在它的內(nèi)部,我們也必須要實現(xiàn)它內(nèi)部未實現(xiàn)的方法。這種方式不僅可以用于創(chuàng)建接口的匿名內(nèi)部類,也可以創(chuàng)建抽象類的匿名內(nèi)部類:

findViewById<TextView>(R.id.tv).setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        //do something
    }
})
//上面的代碼可以用SAM轉(zhuǎn)換簡化,IDE會提示

Java 和 Kotlin 相同的地方就在于,它們的接口與抽象類,都不能直接創(chuàng)建實例。想要創(chuàng)建接口和抽象類的實例,我們必須通過匿名內(nèi)部類的方式。

在 Kotlin 中,匿名內(nèi)部類還有一個特殊之處,就是我們在使用 object 定義匿名內(nèi)部類的時候,其實還可以在繼承一個抽象類的同時,來實現(xiàn)多個接口:

//抽象類和抽象方法
 abstract class Person{
     abstract  fun isAdult()
}
//接口
interface AListener {
    fun getA()
}
//接口
interface BListener {
    fun getB()
}
//繼承一個抽象類的同時,來實現(xiàn)多個接口
private val  item = object :Person(),AListener,BListener{
    override fun isAdult() {
        //do something
    }
    override fun getA() {
        //do something
    }
    override fun getB() {
        //do something
    }
}

在日常的開發(fā)工作當(dāng)中,我們有時會遇到這種情況:我們需要繼承某個類,同時還要實現(xiàn)某些接口,為了達到這個目的,我們不得不定義一個內(nèi)部類,然后給它取個名字。但這樣的類,往往只會被用一次就再也沒有其他作用了。所以針對這種情況,使用 object 的這種語法就正好合適。我們既不用再定義內(nèi)部類,也不用想著該怎么給這個類取名字,因為用過一次后就不用再管了。

引申:可以把函數(shù)當(dāng)做參數(shù)簡化定義接口的操作。以前寫java時應(yīng)該都寫過很多如下的接口回調(diào):

class DownloadFile {
    //攜帶token下載文件
    fun downloadFile(token:String) {
        val filePath = ""
        listener?.onSuccess(filePath)
    }
    //定義成員變量
    private var listener: OnDownloadResultListener? = null
    //寫set方法
    fun setOnDownloadResultListener(listener: OnDownloadResultListener){
        this.listener = listener
    }
    //定義接口
    interface OnDownloadResultListener {
        fun onSuccess(filePath:String)
    }
}

通過函數(shù)當(dāng)做參數(shù)就不需要定義接口了:

class DownloadFile {
    private var onSuccess: ((String?) -&gt; Unit)? = null
    fun downloadFile(token:String) {
        val filePath = ""
        onSuccess?.invoke(filePath)
    }
    fun setOnDownloadResultListener(method:((String?) -&gt; Unit)? = null){
        this.onSuccess = method
    }
}
//調(diào)用
DownloadFile().downloadFile("")
DownloadFile().setOnDownloadResultListener { filePath -&gt;
    print("$filePath")
}

二、單例模式

在 Kotlin 當(dāng)中,要實現(xiàn)單例模式其實非常簡單,我們直接用 object 修飾類即可:

object StringUtils {
    fun getLength(text: String?): Int = text?.length ?: 0
}
//反編譯
public final class StringUtils {
   @NotNull
   public static final StringUtils INSTANCE;  //靜態(tài)單例對象
   public final int getLength(@Nullable String text) {
      return text != null ? text.length() : 0;
   }
   private StringUtils() {
   }
   static {  //靜態(tài)代碼塊
      StringUtils var0 = new StringUtils();
      INSTANCE = var0;
   }
}

這種方式定義的單例模式,雖然簡潔,但存在兩個缺點:

1、不支持懶加載。

2、不支持傳參構(gòu)造單例。寫構(gòu)造方法會報錯,會提示object修飾的類不允許有構(gòu)造方法。

三、伴生對象

1、深入分析伴生對象

Kotlin 當(dāng)中沒有 static 關(guān)鍵字,所以我們沒有辦法直接定義靜態(tài)方法和靜態(tài)變量。不過,Kotlin 還是為我們提供了伴生對象,來幫助實現(xiàn)靜態(tài)方法和變量。

我們先來看看 object 定義單例的一種特殊情況,看看它是如何演變成“伴生對象”的:

class User() {
    object InnerClass {
        fun foo() {}
    }
}

用object修飾嵌套類,看下反編譯的結(jié)果:

public final class User {
   //object修飾的內(nèi)部類為靜態(tài)內(nèi)部類
   public static final class Inner {
      @NotNull
      public static final User.Inner INSTANCE;  //靜態(tài)單例對象
      public final void foo() {
      }
      private Inner() {
      }
      //通過static靜態(tài)代碼塊創(chuàng)建了單例對象
      static {
         User.Inner var0 = new User.Inner();
         INSTANCE = var0;
      }
   }
}

調(diào)用的時候的代碼

User.InnerClass.foo()

可以看到foo方法并不是靜態(tài)方法,那加上@JvmStatic這個注解試試:

class User() {
    object InnerClass {
        @JvmStatic
        fun foo() {}
    }
}
//反編譯結(jié)果
public final class User {
   public static final class InnerClass {
      @NotNull
      public static final User.InnerClass INSTANCE;
      @JvmStatic
      public static final void foo() {   //foo方法變成了靜態(tài)方法
      }
      private InnerClass() {
      }
      static {
         User.InnerClass var0 = new User.InnerClass();
         INSTANCE = var0;
      }
   }
}

foo方法變成了一個靜態(tài)方法,但是在使用的時候還是要User.InnerClass.foo(),而User類中的靜態(tài)方法應(yīng)該是直接User.foo()調(diào)用才對,這還是不符合定義靜態(tài)方法的初衷。那在 Kotlin 如何實現(xiàn)這樣的靜態(tài)方法呢?我們只需要在前面例子當(dāng)中的 object 關(guān)鍵字前面,加一個 companion 關(guān)鍵字即可。

①不加@JvmStatic注解

//假如不加@JvmStatic注解
class User() {
   companion object InnerClass {
        fun foo() {}
    }
}
//反編譯
public final class User {
   @NotNull
   public static final User.InnerClass InnerClass = new User.InnerClass((DefaultConstructorMarker)null);
   public static final class InnerClass {
      public final void foo() {
      }
      private InnerClass() {
      }
      // $FF: synthetic method
      public InnerClass(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}
//調(diào)用
User.foo()
//反編譯調(diào)用的代碼
User.InnerClass.foo();

如果不加上@JvmStatic注解調(diào)用的時候只是省略了前面的單例對象InnerClass,foo仍然不是User的靜態(tài)方法。

②加@JvmStatic注解

//假如加@JvmStatic注解
class User() {
   companion object InnerClass {
       @JvmStatic
        fun foo() {}
    }
}
//反編譯
public final class User {
   @NotNull
   public static final User.InnerClass InnerClass = new User.InnerClass((DefaultConstructorMarker)null);
   @JvmStatic
   public static final void foo() {  //多生成了一個foo方法,但其實還是調(diào)用的下面的foo方法
      InnerClass.foo();
   }
   public static final class InnerClass {
      @JvmStatic
      public final void foo() {   //實際的foo方法
      }
      private InnerClass() {
      }
      // $FF: synthetic method
      public InnerClass(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

可以看到這個時候多生成了一個靜態(tài)的foo方法,可以通過User.foo()真正去調(diào)用了,而不是省略掉了InnerClass單例對象(把InnerClass對象放在了靜態(tài)方法的實現(xiàn)中)。

那又有問題來了,上面二種方式應(yīng)該如何選擇,哪種情況下哪個好,什么時候該加注解什么時候不該加注解?

解析:1、用companion修飾的對象會創(chuàng)建一個Companion的實例:

class User {
   companion object {
        fun foo() {}
    }
}
//反編譯
public final class User {
   @NotNull
   public static final User.Companion Companion = new User.Companion((DefaultConstructorMarker)null);
   public static final class Companion {
      public final void foo() {
      }
      private Companion() {
      }
      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}
//java中調(diào)用
User.Companion.foo();

如果不加@JvmStatic,java調(diào)用kotlin代碼會多創(chuàng)建這個Companion實例,會多一部分內(nèi)存開銷,所以如果這個靜態(tài)方法java需要調(diào)用,那務(wù)必要把@JvmStatic加上。

2、多創(chuàng)建一個靜態(tài)foo方法會不會多內(nèi)存開銷? 答案是不會,因為這個靜態(tài)的foo方法調(diào)用的也是Companion中的方法foo方法,所以不會有多的內(nèi)存開銷。

2、用伴生對象實現(xiàn)工廠模式

所謂的工廠模式,就是指當(dāng)我們想要統(tǒng)一管理一個類的創(chuàng)建時,我們可以將這個類的構(gòu)造函數(shù)聲明成 private,然后用工廠模式來暴露一個統(tǒng)一的方法,以供外部使用。Kotlin 的伴生對象非常符合這樣的使用場景:

// 私有的構(gòu)造函數(shù),外部無法調(diào)用
class User private constructor(name: String) {
    companion object {
     @JvmStatic
         fun create(name: String): User? {
            // 統(tǒng)一檢查,比如敏感詞過濾
            return User(name)
        }
    }
}

3、用伴生對象實現(xiàn)單例模式

(1)、借助懶加載委托

class MainActivity : AppCompatActivity() {
    //借助懶加載委托實現(xiàn)單例
    private val people by lazy { People("張三", 18) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}
//反編譯后
public final class MainActivity extends AppCompatActivity {
   private final Lazy people$delegate;
   private final People getPeople() {
      Lazy var1 = this.people$delegate;
      Object var3 = null;
      return (People)var1.getValue();
   }
   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(1300000);
   }
   public MainActivity() {   //構(gòu)造方法
      this.people$delegate = LazyKt.lazy((Function0)null.INSTANCE);  //lazy方法有線程安全的實現(xiàn)
   }
}

MainActivity的構(gòu)造方法中通過LazyKt.lazy獲取類的代理對象,看下LazyKt.lazy的源碼實現(xiàn):

/**
 * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
 * and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].   //線程安全模式
 *
 * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
 *
 * Note that the returned instance uses itself to synchronize on. Do not synchronize from external code on
 * the returned instance as it may cause accidental deadlock. Also this behavior can be changed in the future.
 */
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
/**
 * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
 * and thread-safety [mode].
 *
 * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
 *
 * Note that when the [LazyThreadSafetyMode.SYNCHRONIZED] mode is specified the returned instance uses itself
 * to synchronize on. Do not synchronize from external code on the returned instance as it may cause accidental deadlock.
 * Also this behavior can be changed in the future.
 */
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

(2)、伴生對象 Double Check

class UserManager private constructor(name: String) {
    companion object {
        @Volatile 
        private var INSTANCE: UserManager? = null
        fun getInstance(name: String): UserManager =
            // 第一次判空
            INSTANCE?: synchronized(this) {
                // 第二次判空
                INSTANCE?:UserManager(name).also { INSTANCE = it }
            }
     }
}
// 使用
UserManager.getInstance("Tom")

我們定義了一個伴生對象,然后在它的內(nèi)部,定義了一個 INSTANCE,它是 private的,這樣就保證了它無法直接被外部訪問。同時它還被注解“@Volatile”修飾了,這可以保證INSTANCE的可見性,而getInstance()方法當(dāng)中的synchronized,保證了INSTANCE的原子性。因此,這種方案還是線程安全的。

同時,我們也能注意到,初始化情況下,INSTANCE 是等于 null 的。這也就意味著,只有在getInstance() 方法被使用的情況下,我們才會真正去加載用戶數(shù)據(jù)。這樣,我們就實現(xiàn)了整個UserManager的懶加載,而不是它內(nèi)部的某個參數(shù)的懶加載。

另外,由于我們可以在調(diào)用getInstance(name) 方法的時候傳入初始化參數(shù),因此,這種方案也是支持傳參的。

單例模式最多的寫法,注意如果參數(shù)是上下文,不能傳遞ActivityFragment的上下文,不然會有內(nèi)存泄漏。(單例的內(nèi)存泄漏)

(3)、抽象類模板

如果有多個類似于上面的單例,那么就會有很多重復(fù)代碼,于是嘗試抽象成模板代碼:

//要實現(xiàn)單例類,就只需要繼承這個 BaseSingleton 即可
//P為參數(shù),T為返回值
abstract class BaseSingleton<in P, out T> {
    @Volatile
    private var instance: T? = null
    //抽象方法,需要我們在具體的單例子類當(dāng)中實現(xiàn)此方法
    protected abstract fun creator(param: P): T
    fun getInstance(param: P): T =
        instance ?: synchronized(this) {
            instance ?: creator(param).also { instance = it }
        }
}

通過伴生對象實現(xiàn)抽象類,并給出具體實現(xiàn)

//構(gòu)建UploadFileManager對象需要一個帶參數(shù)的構(gòu)造方法
class UploadFileManager(val param: String) {
    //伴生對象實現(xiàn)BaseSingleton抽象類
    companion object : BaseSingleton<String, UploadFileManager>() {
        //重寫方法并給出具體實現(xiàn)
        override fun creator(param: String): UploadFileManager {
            return UploadFileManager(param)
        }
    }
    fun foo(){
        print("foo")
    }
}
//調(diào)用
UploadFileManager.getInstance("張三").foo()

因為構(gòu)造方法的限制這種封裝也有一定的局限性。

以上就是kotlin object關(guān)鍵字單例模式實現(xiàn)示例詳解的詳細內(nèi)容,更多關(guān)于kotlin object關(guān)鍵字單例模式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android模擬實現(xiàn)支付寶螞蟻森林效果

    Android模擬實現(xiàn)支付寶螞蟻森林效果

    這篇文章主要為大家詳細介紹了如何利用Android模擬實現(xiàn)支付寶中螞蟻森林的動畫效果,文中的示例代碼講解詳細,感興趣的可以了解一下
    2022-09-09
  • android圖片處理之讓圖片一直勻速旋轉(zhuǎn)

    android圖片處理之讓圖片一直勻速旋轉(zhuǎn)

    讓圖片一直勻速旋,這篇文章主要介紹了android圖片處理之讓圖片一直勻速旋轉(zhuǎn)的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android WebView實現(xiàn)截長圖功能

    Android WebView實現(xiàn)截長圖功能

    這篇文章主要為大家詳細介紹了Android截長圖的一種實現(xiàn)方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Android改變手機屏幕朝向的方法

    Android改變手機屏幕朝向的方法

    這篇文章主要介紹了Android改變手機屏幕朝向的方法,涉及Android動態(tài)更改手機屏幕朝向的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • Android ListView詳解

    Android ListView詳解

    listview控件在項目開發(fā)過程中經(jīng)常會用到,本文給大家分享android listview相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12
  • Android中EditText的drawableRight屬性設(shè)置點擊事件

    Android中EditText的drawableRight屬性設(shè)置點擊事件

    這篇文章主要介紹了Android中EditText的drawableRight屬性的圖片設(shè)置點擊事件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 淺析Android錄屏 MediaRecorder

    淺析Android錄屏 MediaRecorder

    MediaRecorder類是Android sdk提供的一個專門用于音視頻錄制,一般利用手機麥克風(fēng)采集音頻,攝像頭采集圖片信息。這篇文章主要介紹了Android錄屏 MediaRecorder介紹,需要的朋友可以參考下
    2020-03-03
  • Android實現(xiàn)上傳頭像

    Android實現(xiàn)上傳頭像

    這篇文章主要為大家詳細介紹了Android實現(xiàn)上傳頭像,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Android 國際貨幣格式化的示例代碼

    Android 國際貨幣格式化的示例代碼

    本篇文章主要介紹了Android 國際貨幣格式化的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • android通過Location API顯示地址信息的實現(xiàn)方法

    android通過Location API顯示地址信息的實現(xiàn)方法

    這篇文章主要介紹了android通過Location API顯示地址信息的方法,涉及Android操作Geocoder類獲取地址信息的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07

最新評論