Kotlin项目开发实践二

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

1.单例实现的三种方式

  • 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实现形式,其中:

  1. @Volatile保证代码指令有序性
  2. getInstance方法内外层singleton判空保证singleton已经初始化完成了,线程不要额外再去竞争锁
  3. getInstance方法内内层singleton判空保证如果之前线程已经初始化singleton完成了,后续的线程不要再重复初始化了

可以看到,这是个线程安全且懒汉式方式实现的单例

2.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()
复制代码

猜你喜欢

转载自juejin.im/post/7082560219615592456