在Kotlin中,给我们引入了关于“惰性初始化”(lazy initialization)这一古老概念的两个新特性,比如将一个变量的初始化延迟到之后的某个时刻。这是一个非常便利的特性,因为我们可以不用初始化直到需要使用它,或者仅仅是因为我们还不具备所有的条件去初始化它。
那让我们先从句法上认识它俩吧。
Initialization by Lazy
val myUtil by lazy {
MyUtil(parameter1, parameter2)
}
上述代码初始化一个MyUtil对象,但这个动作只会在初次使用myUtil时才会进行。
Late initialization
lateinit var myUtil: MyUtil
一个函数里的某个地方
myUtil = MyUtil(parameter1, parameter2)
上述代码延迟啦初始化myUtil的过程直到我们去初始化它的时候
为什么有两个,使用场景是啥?
即便两者有着相同的概念,但还是有本质上的区别的。仅从变量类型上来看,一个为val(不可变),另一个为var(可变)。不言自明吧。以下是一些典型的使用场景。
单例
有时候,我们只需要一个变量实例,其余共享这个变量。
MyUtil getMyUtil() {
if (myUtil == null) myUtil = new MyUtil(param1, param2);
return myUtil;
}
这段代码用来写单例相当方便,确保我们对该变量的访问都是同一个对象。同时它也使得我们需要使用它时才去创建此对象。如此场景,Lazy Initialization便有了用武之地。
初始化一个不可为空(Non-nullable)的变量
Kotlin中,我们定义一个变量的时候必须得声明它可否为空。这有助于编译器在编译时识别出潜在的空对象使用,从而避免空指针异常。
所以对于一个不可为空的成员变量,其类创建之时,它必须设为一个非空值。如果在构造此类事,我们具有所有的依赖,这是没问题的。不幸的是,有些依赖只在之后的某个时刻可用(比如上述param1,param2)。我们从而陷入一个死锁的困境,我们既不能一开始将其设为null(Kotlin可不同意),但我们又必须等到我们得到所有的依赖时才可为其赋值。
莫方,lateinit正向你招手,它允许你在声明变量时可以不对其初始化,直到我们得到所需的依赖对象时再去初始化它也可以。
依赖注入变量
如果我们在KotlIn中使用依赖注入框架(比如Dagger2),所声明的变量不能进行初始化。因此lateinit可以保证该变量之后将被初始化。
@Inject
lateinit var myUtil: MyUtil
事实上,lateinit 正是为此被Kotlin引入的。
什么时候使用呢?
上述例子只涉及到了很少的一些场景。你可以将其运用到更多的场景中,只要他们是不可为空的变量。以下是一些简单的规则帮助你决定使用哪一个。
1.如果该变量可变,使用lateinit
2.如果可以通过外部设置(比如传递外部变量去设置它),使用lateinit。也可以通过lazy,但就没那么直接了。
3.如果只需初始化一次并且为其他共享,更多的是通过内部来设置的话,使用lazy。当然你也可以使用lateinit,但是使用lazy能更好滴封装你的初始化代码。
简言之,对于变量的初始化,懒为上,迟次之,声明时最次。最好是懒一点囖。比尔盖茨说过。
I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.” ~Bill Gates