Kotlin 单例模式

单例模式是一种常见的设计模式,它确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的写法有好几种,Kotlin中如何实现呢?

Kotlin 单例模式

事实上,对象就是具有单一实现的数据类型。所以如果我们想在Java中找到类似的东西,那将是单例模式。而使用Kotlin又是如何实现呢?下面我们来做一下比较。

Java 方式

让我们先回顾一下Java的单例模式写法。

第一种(懒汉,线程不安全):

/**
 * Created by ngudream on 17-6-10.
 */
public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}   
      public static Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
      }  
 }  

第二种(懒汉,线程安全):

/**
 * Created by ngudream on 17-6-10.
 */
public class Singleton {  
     private static Singleton instance;  
     private Singleton (){}
     public static synchronized Singleton getInstance() {  
     if (instance == null) {  
         instance = new Singleton();  
     }  
     return instance;  
     }  
 }  

第三种(饿汉):

public class Singleton {  
      private Singleton instance = null;  
      static {  
      instance = new Singleton();  
      }  
      private Singleton (){}
      public static Singleton getInstance() {  
      return this.instance;  
      }  
 }  

第四种(双重校验锁):

/**
 * Created by ngudream on 17-6-10.
 */
public class Singleton {  
      private volatile static Singleton singleton;  
      private Singleton (){}   
      public static Singleton getSingleton() {  
      if (singleton == null) {  
          synchronized (Singleton.class) {  
          if (singleton == null) {  
              singleton = new Singleton();  
          }  
         }  
     }  
     return singleton;  
     }  
 }  

还有好几种写法,有的是线程安全的,有的是线程不安全的。但我们一般应该使用线程安全的写法,推荐是双重检验锁写法。

双重锁后为什么还要添加volatile?
假设没有关键字volatile的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行instance = new Instance(),该构造方法是一个非原子操作,编译后生成多条字节码指令,由于JAVA的指令重排序,可能会先执行instance的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后instance便不为空了,但是实际的初始化操作却还没有执行,如果就在此时线程B进入,就会看到一个不为空的但是不完整(没有完成初始化)的Instance对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。

Kotlin object方式

在kotlin 中通过object关键字定义单例类,这个就比较简单了,代码量相对Java来说变得很少了。

/**
 * Created by ngudream on 17-6-10.
 */

object Singleton {
    fun test(){
        println("hello kotlin!")
    }
}

//调用
Singleton.test()

是不是很简洁?看上去代码量很少,Kotlin内部又是如何实现的呢?我们可以通过AndroidStudio IDE工具栏的Tools->Kotlin->Show Kotlin ByteCode,然后点击弹出界面的Decompile按钮,就可以看到如下代码:

import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 6},
   bv = {1, 0, 1},
   k = 1,
   d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"},
   d2 = {"Lcom/xxx/example/kotlin/Singleton;", "", "()V", "test", "", "production sources for module app"}
)
public final class Singleton {
   public static final Singleton INSTANCE;

   public final void test() {
      String var1 = "hello kotlin!";
      System.out.println(var1);
   }

   private Singleton() {
      INSTANCE = (Singleton)this;
   }

   static {
      new Singleton();
   }
}

是不是很熟悉的赶脚?和上面提到的Java饿汉模式类似。

Kotlin 显式声明静态instance变量

这种方式就是在类内部定义个类对象的变更,然后初始化。

/**
 * Created by ngudream on 17-6-10.
 */
class Singleton private constructor(){

    companion object {
        val instance = Singleton()
    }

    fun test(){
        println("hello kotlin!")
    }
}

查看Kotlin的源码,内部就是直接定义了一个static的变量:

public final class Singleton {
   @NotNull
   private static final Singleton instance = new Singleton();//直接定义变量
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);

   public final void test() {
      String var1 = "hello kotlin!";
      System.out.println(var1);
   }

   public static final class Companion {
      @NotNull
      public final Singleton getInstance() {
         return Singleton.instance;
      }

      private Companion() {
      }

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

如果Singleton并没有实质的调用就初始化,会造成资源的浪费,所以我们可以通过懒汉式加载,等到第一次使用到Singleton内部方法时再初始化。

/**
 * Created by ngudream on 17-6-10.
 * 懒汉式加载
 */

class Singleton private constructor(){

    private object Holder { val INSTANCE = Singleton() }

    companion object {
        val instance: Singleton by lazy { Holder.INSTANCE }
    }

    fun test(){
        println("hello kotlin!")
    }
}

类似的,让我们来看看Kotlin内部是如何实现的:

public final class Singleton {
   @NotNull
   private static final Lazy instance$delegate;
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);

   public final void test() {
      String var1 = "hello kotlin!";
      System.out.println(var1);
   }

   static {
      instance$delegate = LazyKt.lazy((Function0)null.INSTANCE);//先创建代理对象实例
   }

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

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

      private Holder() {
         INSTANCE = (Singleton.Holder)this;
         INSTANCE = new Singleton();
      }

      static {
         new Singleton.Holder();
      }
   }

   public static final class Companion {
      // $FF: synthetic field
      static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Singleton.Companion.class), "instance", "getInstance()Lcom/xxx/example/kotlin/Singleton;"))};

      @NotNull
      public final Singleton getInstance() {
         Lazy var1 = Singleton.instance$delegate;//代码对象获取实际对象
         KProperty var3 = $$delegatedProperties[0];
         return (Singleton)var1.getValue();
      }

      private Companion() {
      }

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

我们再回顾一下Java单例模式线程安全的写法,在Kotlin中又是如何实现的呢?

/**
 * Created by ngudream on 17-6-10.
 * 加锁线程安全
 */
class Singleton private constructor(){

    companion object {
        @Volatile
        var instance: Singleton? = null
            get() {
                if (instance == null) {
                    synchronized(Singleton::class.java) {
                        if (instance == null) {
                            instance = Singleton()
                        }
                    }
                }
                return instance!!
            }
    }

    fun test() {
        println("hello kotlin!")
    }
}

再看一下Kotlin的源码实现:

public final class Singleton {
   @Nullable
   private static volatile Singleton instance;
   public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);

   public final void test() {
      String var1 = "hello kotlin!";
      System.out.println(var1);
   }

   private Singleton() {
   }

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

   // $FF: synthetic method
   @Nullable
   public static final Singleton access$getInstance$cp() {
      return instance;
   }

   public static final class Companion {
      @Nullable
      public final Singleton getInstance() {
         if(Singleton.Companion.getInstance() == null) {
            Class var1 = Singleton.class;
            synchronized(var1){}//竟然没做事情??

            try {
               if(Singleton.Companion.getInstance() == null) {
                  Singleton.Companion.setInstance(new Singleton((DefaultConstructorMarker)null));
               }

               Unit var3 = Unit.INSTANCE;
            } finally {
               ;
            }
         }

         Singleton var10000 = Singleton.Companion.getInstance();
         if(var10000 == null) {
            Intrinsics.throwNpe();
         }

         return var10000;
      }

      public final void setInstance(@Nullable Singleton var1) {
         Singleton.instance = var1;
      }

      private Companion() {
      }

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

通过源码,我们发现Kotlin 的synchronized并没有做事情,什么鬼?是否达到了真正的线程安全呢?这个还有待我找时间认真考证一下。

当然,对单例的初始化我们还可以这样写,以Android中的Application为例:

class App : Application() {
    companion object {
        lateinit var instance: App //延迟加载,不需要初始化,否则需要在构造函数初始化
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

}
文章目录
  1. 1. Kotlin 单例模式
    1. 1.1. Java 方式
    2. 1.2. Kotlin object方式
    3. 1.3. Kotlin 显式声明静态instance变量
|