Kotlin下的5种单例模式
前⾔
最近在学习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的字节码。
查看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实现中,我们让其主构造函数私有化并⾃定义了其属性访问器,其余内容⼤同⼩异。
如果有⼩伙伴不清楚Kotlin构造函数的使⽤⽅式。请点击 - - -
不清楚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注解。
四、双重校验锁式(Double Check)
//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() 只是返回记录的结果。
这⾥还有有两个额外的知识点。
⾼阶函数,⾼阶函数是将函数⽤作参数或返回值的函数(我很纠结我到底讲不讲,哎)。⼤家还是看这个 ---
如果你了解以上知识点,我们直接来看Lazy的内部实现。
Lazy内部实现
public fun<T>lazy(mode: LazyThreadSafetyMode, initializer:()-> T): Lazy<T>=
when(mode){
LazyThreadSafetyMode.SYNCHRONIZED ->SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION ->SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE ->UnsafeLazyImpl(initializer)
}
观察上述代码,因为我们传⼊的mode = LazyThreadSafetyMode.SYNCHRONIZED,
那么会直接⾛ SynchronizedLazyImpl,我们继续观察SynchronizedLazyImpl。
Lazy接⼝
SynchronizedLazyImpl实现了Lazy接⼝,Lazy具体接⼝如下:
public interface Lazy<out T>{
//当前实例化对象,⼀旦实例化后,该对象不会再改变
public val value: T
//返回true表⽰,已经延迟实例化过了,false 表⽰,没有被实例化,
//⼀旦⽅法返回true,该⽅法会⼀直返回true,且不会再继续实例化
public fun isInitialized(): Boolean
}
继续查看SynchronizedLazyImpl,具体实现如下:
SynchronizedLazyImpl内部实现
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
//判断是否已经初始化过,如果初始化过直接返回,不在调⽤⾼级函数内部逻辑
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!!()//调⽤⾼级函数获取其返回值
_value = typedValue  //将返回值赋值给_value,⽤于下次判断时,直接返回⾼级函数的返回值
initializer =null
typedValue
单例模式的几种实现方式}
}
}
//省略部分代码
}
通过上述代码,我们发现 SynchronizedLazyImpl 覆盖了Lazy接⼝的value属性,并且重新了其属性访问器。其具体逻辑与Java的双重检验是类似的。
到⾥这⾥其实⼤家还是肯定有疑问,我这⾥只是实例化了SynchronizedLazyImpl对象,并没有进⾏值的获取,它是怎么拿到⾼阶函数的返回值呢?。这⾥⼜涉及到了委托属性。
委托属性语法是: val/var <;属性名>: <;类型> by <;表达式>。在 by 后⾯的表达式是该 委托, 因
为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() ⽅法。 属性的委托不必实现任何的接⼝,但是需要提供⼀个 getValue() 函数(和 setValue()——对于var 属性)。
⽽Lazy.kt⽂件中,声明了Lazy接⼝的getValue扩展函数。故在最终赋值的时候会调⽤该⽅法。
@kotlin.internal.InlineOnly
//返回初始化的值。
public inline operator fun<T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
五、静态内部类式
//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 }
}
}
}
其中关于?:操作符,如果 ?: 左侧表达式⾮空,就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧为空时,才会对右侧表达式求值。
观察代码我们可以发现⼤致上和我们的Java中的Double check是⼀样的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。