object
先看代码:object Pro {
}
就这么简单实现了Pro的单例,我们可以反编译成java代码看下具体的实现原理:
public final class Pro {
@NotNull
public static final Pro INSTANCE;
private Pro() {
}
static {
Pro var0 = new Pro();
INSTANCE = var0;
}
}
首先Pro
类的构造方法声明为private
,其次可以看到这就是通过静态代码块实现的单例,利用类静态代码块只会执行一次的特性,属于线程安全且饿汉式
的;
java中通过Pro.INSTANCE
获取这个单例,而kotlin直接通过Pro
获取单例
lazy
先上代码:
class Pro private constructor() {
companion object {
val INSTANCE by lazy {
Pro()
}
}
}
主要是利用了伴生对象声明的属性为静态变量,且lazy默认的实现模式是加锁线程安全的,这是个线程安全且懒汉式
单例实现,关于lazy
想要了解更多可以参考Kotlin开发实践之一
@Volatile
var singleton: Pro? = null
fun getInstance(): Pro {
if (singleton == null) {
synchronized(Pro::class.java) {
if (singleton == null) {
singleton = Pro()
}
}
}
return singleton!!
}
上面就是java双重检查锁的kotlin实现形式,其中:
@Volatile
保证代码指令有序性getInstance
方法内外层singleton
判空保证singleton
已经初始化完成了,线程不要额外再去竞争锁getInstance
方法内内层singleton
判空保证如果之前线程已经初始化singleton
完成了,后续的线程不要再重复初始化了线程安全且懒汉式
方式实现的单例typealias
给复杂类型取个别名这个typealias
关键字主要是用于给类型取个别名,下面介绍下两种使用的场景:
//拼接Int和String类型并返回String类型
val block: ((Int, String) -> String)? = null
这个函数类型(Int, String) -> String)写起来很麻烦且可读性很差,这个时候就到了typealias上传的时候了:
typealias Concat = (Int, String) -> String
val block: Concat? = null
将(Int, String) -> String)
取别名为Concat
,不仅使用起来很方便,还容易看出这个函数类型的使用场景:拼接
ViewModel
时,我们可能经常会对接口的返回进行如下封装:class MainViewModel: ViewModel() {
val data: MutableLiveData<Response<String>> = MutableLiveData()
fun test() {
data.value = Response("haha")
}
data class Response<T>(val data: T? = null)
}
使用Response
对服务器返回进行封装,泛型T
表示响应数据可以反序列化成的实体类。
可以看到上面,每定义一个MutableLiveData
都得在其泛型中声明Response<T>
,由于我们这个Response
是对所有接口响应的统一封装,是一个确定的类型,而Response<T>
中的T
才是每次创建MutableLiveData
需动态化指定的类型。
那Response<T>中的Response可不可以省略呢,这个时候就到了typealias上传的时候了:
typealias ExternalLiveData<T> = MutableLiveData<MainViewModel.Response<T>>
这样每次再创建MutableLiveData就可以这样写:
val data1: ExternalLiveData<String> = MutableLiveData()
阅读量:2017
点赞量:0
收藏量:0