Java 和Kotlin的单例模式其实很像,只是Kotlin一部分单例可以用对象类和委托lazy来实现
/**
* 懒汉式,线程不安全
*/
class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
/**
* 懒汉式,线程安全
*/
class Singleton1 {
private static Singleton1 instance;
private Singleton1() {
}
public static synchronized Singleton1 getInstance() {
if (instance == null) {
instance = new Singleton1();
}
return instance;
}
}
/**
* 饿汉式
*/
class Singleton2 {
private static Singleton2 instance = new Singleton2();
private Singleton2() {
}
public static Singleton2 getInstance() {
return instance;
}
}
/**
* 双检锁/双重校验锁(DCL,即 double-checked locking)
*/
class Singleton3 {
private volatile static Singleton3 singleton;
private Singleton3() {
}
public static Singleton3 getSingleton() {
if (singleton == null) {
synchronized (Singleton3.class) {
if (singleton == null) {
singleton = new Singleton3();
}
}
}
return singleton;
}
}
/**
* 登记式/静态内部类
*/
class Singleton4 {
private static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
private Singleton4() {
}
public static final Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
}
/**
* 枚举
*/
enum Singleton5 {
INSTANCE2;
public void whateverMethod() {
System.out.println("....");
}
}
/**
* 懒汉式,线程不安全,仿Java
*/
internal class Singleton6 private constructor() {
companion object {
private var instance: Singleton6? = null
fun getInstance(): Singleton6 {
if (instance == null) {
instance = Singleton6()
}
return instance!!
}
}
}
/**
* 懒汉式,线程不安全
*/
internal class Singleton7 private constructor() {
companion object {
var instance: Singleton7? = null
get() {
if (field == null) {
field = Singleton7()
}
return field
}
}
}
/**
* 懒汉式,线程安全
*/
internal class Singleton8 private constructor() {
companion object {
@get:Synchronized
var instance: Singleton8? = null
get() {
if (field == null) {
field = Singleton8()
}
return field
}
}
}
/**
*饿汉式
*/
internal object Singleton9 {
fun whateverMethod() {
println("....")
}
}
/**
*饿汉式,仿Java
*/
internal class Singleton10 private constructor() {
companion object {
val instance = Singleton10()
}
}
/**
* 双检锁/双重校验锁(DCL,即 double-checked locking)
*/
internal class Singleton12 private constructor() {
companion object {
val instance: Singleton12 by lazy {
Singleton12()
}
}
}
/**
* 双检锁/双重校验锁(DCL,即 double-checked locking),仿Java
*/
internal class Singleton11 private constructor() {
companion object {
@Volatile
var instance: Singleton11? = null
get() {
if (field == null) {
synchronized(Singleton11::class) {
if (field == null) {
field = Singleton11()
}
}
}
return field
}
}
}
/**
* 登记式/静态内部类
*/
internal class Singleton13 {
private object SingletonHolder {
val INSTANCE = Singleton13()
}
companion object {
val instance: Singleton13
get() = SingletonHolder.INSTANCE
}
}
/**
* 枚举
*/
internal enum class Singleton14 {
INSTANCE;
fun whateverMethod() {
println("....")
}
}
最近在学习Kotlin这门语言,在项目开发中,运用到了单例模式。因为其表达方式与Java是不同的。所以对不同单例模式的实现进行了分别探讨。主要单例模式实现如下:
PS:该篇文章不讨论单例模式的运用场景与各种模式下的单例模式的优缺点。只讨论在Java下不同单例模式下的对应Kotlin实现。
//Java实现
public class SingletonDemo {
private static SingletonDemo instance=new SingletonDemo();
private SingletonDemo(){
}
public static SingletonDemo getInstance(){
return instance;
}
}
//Kotlin实现
object SingletonDemo
这里很多小伙伴就吃了一惊。我靠,一个object 关键字就完成相同的功能?一行代码?
学习了Kotlin的小伙伴肯定知道,在Kotlin中类没有静态方法。如果你需要写一个可以无需用一个类的实例来调用,但需要访问类内部的函数(例如,工厂方法,单例等),你可以把该类声明为一个对象。该对象与其他语言的静态成员是类似的。如果想了解Kotlin对象声明的更多内容,可以去看看之前的文章。
到这里,如果还是有很多小伙伴不是很相信一行代码就能解决这个功能,我们可以通过一下方式查看Kotlin的字节码。
查看Kotlin对应字节码
我们进入我们的Android Studio(我的Android Studio 3.0,如果你的编译器版本过低,请自动升级) 选择Tools工具栏,选择"Kotlin",选择“Show Kotlin Bytecode"
选择过后就会进入到下方界面:
点击"Decompile" 根据字节码得到以下代码:
public final class SingletonDemo {
public static final SingletonDemo INSTANCE;
private SingletonDemo(){}
static {
SingletonDemo var0 = new SingletonDemo();
INSTANCE = var0;
}
}
通过以上代码,我们了解事实就是这个样子的,使用Kotlin"object"进行对象声明与我们的饿汉式单例的代码是相同的。
//Java实现
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
//Kotlin实现
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
fun get(): SingletonDemo{
//细心的小伙伴肯定发现了,这里不用getInstance作为为方法名,是因为在伴生对象声明时,内部已有getInstance方法,所以只能取其他名字
return instance!!
}
}
}
上述代码中,我们可以发现在Kotlin实现中,我们让其主构造函数私有化并自定义了其属性访问器,其余内容大同小异。
//Java实现
public class SingletonDemo {
private static SingletonDemo instance;
private SingletonDemo(){}
public static synchronized SingletonDemo getInstance(){//使用同步锁
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
}
//Kotlin实现
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!!
}
}
}
大家都知道在使用懒汉式会出现线程安全的问题,需要使用使用同步锁,在Kotlin中,如果你需要将方法声明为同步,需要添加@Synchronized注解。
//Java实现
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实现
class SingletonDemo private constructor() {
companion object {
val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonDemo() }
}
}
小伙伴们惊不惊喜,感不感动啊。我们居然几行代码就实现了多行的Java代码。其中我们运用到了Kotlin的延迟属性 Lazy。
Lazy是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果,后续调用 get() 只是返回记录的结果。
这里还有有两个额外的知识点。
//Java实现
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实现
class SingletonDemo private constructor() {
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder= SingletonDemo()
}
}
静态内部类的实现方式,也没有什么好说的。Kotlin与Java实现基本雷同。
那么如果小伙伴问,如何在Kotlin版的Double Check,给单例添加一个属性,这里给大家提供了一个实现的方式:
class SingletonDemo private constructor(private val property: Int) {
//这里参数可以根据实际需求发生改变
companion object {
@Volatile private var instance: SingletonDemo? = null
fun getInstance(property: Int) =
instance ?: synchronized(this) {
instance ?: SingletonDemo(property).also { instance = it }
}
}
}
其中关于?:操作符,如果 ?: 左侧表达式非空,就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧为空时,才会对右侧表达式求值。