在Kotlin中加矛,給我們引入了關(guān)于“惰性初始化”(lazy initialization)這一古老概念的兩個新特性履婉,比如將一個變量的初始化延遲到之后的某個時刻。這是一個非常便利的特性斟览,因為我們可以不用初始化直到需要使用它毁腿,或者僅僅是因為我們還不具備所有的條件去初始化它。
那讓我們先從句法上認識它倆吧苛茂。
Initialization by Lazy
val myUtil by lazy {
MyUtil(parameter1, parameter2)
}
上述代碼初始化一個MyUtil對象已烤,但這個動作只會在初次使用myUtil時才會進行稽荧。
Late initialization
lateinit var myUtil: MyUtil
一個函數(shù)里的某個地方
myUtil = MyUtil(parameter1, parameter2)
上述代碼延遲啦初始化myUtil的過程直到我們?nèi)コ跏蓟臅r候
為什么有兩個际邻,使用場景是啥?
即便兩者有著相同的概念泼舱,但還是有本質(zhì)上的區(qū)別的躁绸。僅從變量類型上來看裕循,一個為val(不可變),另一個為var(可變)净刮。不言自明吧剥哑。以下是一些典型的使用場景。
單例
有時候淹父,我們只需要一個變量實例株婴,其余共享這個變量。
MyUtil getMyUtil() {
if (myUtil == null) myUtil = new MyUtil(param1, param2);
return myUtil;
}
這段代碼用來寫單例相當(dāng)方便暑认,確保我們對該變量的訪問都是同一個對象困介。同時它也使得我們需要使用它時才去創(chuàng)建此對象。如此場景穷吮,Lazy Initialization便有了用武之地逻翁。
初始化一個不可為空(Non-nullable)的變量
Kotlin中,我們定義一個變量的時候必須得聲明它可否為空捡鱼。這有助于編譯器在編譯時識別出潛在的空對象使用八回,從而避免空指針異常。
所以對于一個不可為空的成員變量,其類創(chuàng)建之時缠诅,它必須設(shè)為一個非空值溶浴。如果在構(gòu)造此類事,我們具有所有的依賴管引,這是沒問題的士败。不幸的是,有些依賴只在之后的某個時刻可用(比如上述param1,param2)褥伴。我們從而陷入一個死鎖的困境谅将,我們既不能一開始將其設(shè)為null(Kotlin可不同意),但我們又必須等到我們得到所有的依賴時才可為其賦值重慢。
莫方饥臂,lateinit正向你招手,它允許你在聲明變量時可以不對其初始化似踱,直到我們得到所需的依賴對象時再去初始化它也可以隅熙。
依賴注入變量
如果我們在KotlIn中使用依賴注入框架(比如Dagger2),所聲明的變量不能進行初始化。因此lateinit可以保證該變量之后將被初始化核芽。
@Inject
lateinit var myUtil: MyUtil
事實上囚戚,lateinit 正是為此被Kotlin引入的。
什么時候使用呢轧简?
上述例子只涉及到了很少的一些場景驰坊。你可以將其運用到更多的場景中,只要他們是不可為空的變量吉懊。以下是一些簡單的規(guī)則幫助你決定使用哪一個庐橙。
1.如果該變量可變,使用lateinit
2.如果可以通過外部設(shè)置(比如傳遞外部變量去設(shè)置它)借嗽,使用lateinit态鳖。也可以通過lazy,但就沒那么直接了恶导。
3.如果只需初始化一次并且為其他共享浆竭,更多的是通過內(nèi)部來設(shè)置的話,使用lazy惨寿。當(dāng)然你也可以使用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