Kotlin设计模式(1)单例模式

  • 饿汉模式
  • 懒汉模式
  • 线程安全的懒汉模式
  • 双重校验锁的懒汉模式
  • 静态内部类的懒汉模式

  单例模式是开发中最常用的设计模式,Kotlin 中实现单例模式是很简单的,下面对比下 KotlinJava 单例模式实现。

一、饿汉模式

  • Java -饿汉模式
// 单例-饿汉模式
public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton get() { return instance; }
}
  • Kotlin -饿汉模式
// 单例-饿汉模式
object Singleton

  Kotlin 中实现 饿汉模式 的单例是非常简单的,通过关键字 object + 类名 即可实现。

  • Java -Kotlin 转换 Java

  为了验证 object + 类名 即可实现 kotlin 版本的单例模式,我们通过 Android studio 编译器将 Kotlin 代码转换为 Java 代码,如下:

public final class Singleton {
   public static final Singleton INSTANCE; 

   private Singleton() { }

   // 在类加载时执行
   static {
      Singleton var0 = new Singleton(); 
      INSTANCE = var0;
   }
}

  从转换后的代码中可以看出,定义的只读静态成员变量 INSTANCE 是在静态代码块中初始化,也就是说在类加载时已经对成员变量进行了初始化,同时构造方法为私有满足 饿汉模式 的单例特点,因此 object + 类名 实际是一个 饿汉模式 的单例。

二、懒汉模式

  • Java -懒汉模式
// 单例-懒汉模式
public class Singleton {
    private static volatile Singleton instance = null;

    private Singleton() { }

    public static Singleton get() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • Kotlin -懒汉模式
// 单例-懒汉模式
class Singleton private constructor() {
    companion object {
        private var instance: Singleton? = null
            get() {
                if (field == null) {
                    field = Singleton()
                }
                return field
            }

        @JvmStatic
        fun get(): Singleton = instance!!
    }
}
  • Java -Kotlin 转换 Java
public final class Singleton {
   private static Singleton instance;  // ①
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);  // ③

   private Singleton() {
   }

   // $FF: synthetic method
   public Singleton(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   @JvmStatic
   @NotNull
   public static final Singleton get() {  // ②
      return Companion.get();
   }

   public static final class Companion {
      private final Singleton getInstance() {  // ⑤
         if (Singleton.instance == null) {
            Singleton.instance = new Singleton((DefaultConstructorMarker)null);
         }

         return Singleton.instance;
      }

      private final void setInstance(Singleton var1) {
         Singleton.instance = var1;
      }

      @JvmStatic
      @NotNull
      public final Singleton get() {  // ④
         Singleton var10000 = ((Singleton.Companion)this).getInstance();
         if (var10000 == null) {
            Intrinsics.throwNpe();
         }

         return var10000;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

  代码第②行构建了一个 Companion 对象的静态成员变量,在 Companion 对象中提供了 get() 方法获得 Singleton 实例,而在此 get() 方法中实际是调用代码⑤真正的创建 Singleton 实例,同时将此实例赋值给代码代码①的静态成员变量。从代码实现上看与我们上面的 Java 实现是一致的,所以上面的 kotlin 实现是 懒汉模式 的单例。

三、线程安全的懒汉模式

  • Java -线程安全的懒汉模式
// 单例-线程安全的懒汉模式
public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() { }

    public static Singleton get() {
        synchronized (Singleton.class) {
            if (instance == null)
                instance = new Singleton();
        }
        return instance;
    }

}
  • Kotlin -线程安全的懒汉模式
// 单例-线程安全的懒汉模式
class Singleton private constructor() {
    companion object {
        private var instance: Singleton? = null
            get() {
                if (field == null) {
                    field = Singleton()
                }
                return field
            }

        @JvmStatic
        @Synchronized
        fun get(): Singleton = instance!!
    }
}
  • Java -Kotlin 转换 Java
public final class Singleton {
   private static Singleton instance;
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);

   private Singleton() {
   }

   // $FF: synthetic method
   public Singleton(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   @JvmStatic
   @NotNull
   public static final synchronized Singleton get() {  // 1️⃣
      return Companion.get();
   }

   public static final class Companion {
      private final Singleton getInstance() {
         if (Singleton.instance == null) {
            Singleton.instance = new Singleton((DefaultConstructorMarker)null);
         }

         return Singleton.instance;
      }

      private final void setInstance(Singleton var1) {
         Singleton.instance = var1;
      }

      @JvmStatic
      @NotNull
      public final synchronized Singleton get() {  // 2️⃣
         Singleton var10000 = ((Singleton.Companion)this).getInstance();
         if (var10000 == null) {
            Intrinsics.throwNpe();
         }

         return var10000;
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

  从代码可以看出,线程安全的懒汉模式转后的代码与懒汉模式转后的代码基本一致,只是比懒汉模式在代码1️⃣2️⃣中多出一个 synchronized 关键字修饰方法,保证方法在同一时间只有一个线程在访问。与我们实现 Java 版本的线程安全的懒汉模式是一样的,间接的验证了 Kotlin 代码实现的正确性。

四、双重校验锁的懒汉模式

  • Java -双重校验锁的懒汉模式
// 单例-双重校验锁的懒汉模式
public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() { }

    public static Singleton get() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}
  • Kotlin -双重校验锁的懒汉模式
// 单例-双重校验锁的懒汉模式
class Singleton private constructor() {
    companion object {
        val instance: Singleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Singleton() }
    }
}

  kotlin 实现双重校验锁单例主要是通过关键字 lazy 实现的,所以此处应该看下 SYNCHRONIZED 模式下的 lazy 实现。

public actual fun  lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

  SYNCHRONIZED 模式实际调用的是 SynchronizedLazyImpl(initializer),来看下SynchronizedLazyImpl 的实现:

private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, 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
            if (_v1 !== UNINITIALIZED_VALUE) {  // 1️⃣
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {  // 2️⃣
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {  // 3️⃣
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}

  源码实现可以看出,SynchronizedLazyImpl 的核心代码是属性 valueget 访问器的实现。在 get 访问器中实现了双重校验锁的功能,代码第1️⃣3️⃣对应双重效验,代码第2️⃣进行加锁效验,防止 initializer 的多次调用。

五、静态内部类的懒汉模式

  • Java -静态内部类的懒汉模式
// 单例-静态内部类的懒汉模式
public class Singleton {

    private Singleton() { }

    public static Singleton get() {
        return Holder.INSTANCE;
    }

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

}
  • Kotlin -静态内部类的懒汉模式
// 单例-静态内部类的懒汉模式
class Singleton private constructor() {

    companion object {
        @JvmStatic
        fun get(): Singleton = Holder.INSTANCE
    }

    private object Holder {
        val INSTANCE = Singleton()
    }

}
  • Java -Kotlin 转换 Java
public final class Singleton {
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);

   private Singleton() {
   }

   // $FF: synthetic method
   public Singleton(DefaultConstructorMarker $constructor_marker) {
      this();
   }

   @JvmStatic
   @NotNull
   public static final Singleton get() {
      return Companion.get();
   }

   private static final class Holder {
      @NotNull
      private static final Singleton INSTANCE;
      public static final Singleton.Holder INSTANCE;

      @NotNull
      public final Singleton getINSTANCE() {
         return INSTANCE;
      }

      static {
         Singleton.Holder var0 = new Singleton.Holder();
         INSTANCE = var0;
         INSTANCE = new Singleton((DefaultConstructorMarker)null);
      }
   }

   public static final class Companion {
      @JvmStatic
      @NotNull
      public final Singleton get() {
         return Singleton.Holder.INSTANCE.getINSTANCE();
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

  上述代码可以看出 Holder 对象是 Singleton 的静态内部类,同时在 Holder 的静态代码块中构建了 Singleton 的静态对象,因此只有在 Singleton 对象中提供一个能访问 Holder 对象中 INSTANCE 方法既可以实现静态内部类的单例,Holder 对象中提供了方法 getINSTANCE() 访问成员 INSTANCE,而 Companion 对象的 get 方法是调用 HoldergetINSTANCE() 方法返回 Singleton 对象,最后通过 Singleton 对象本身 get() 调用 Companion.get() 访问 Singleton 实例。

你可能感兴趣的:(Kotlin设计模式(1)单例模式)