前言
我們都知道倔监,在kotlin
中聲明不為空類型變量都需要立即進(jìn)行初始化比搭,不管是可變的還是不可變的。但是 Activity
和Fragment
對(duì)象的創(chuàng)建和視圖的加載是分開進(jìn)行的竖般,所以我們不能直接初始化一些控件咒唆。那么如何處理這種情況呢届垫?
- 聲明可空類型。但是你會(huì)發(fā)現(xiàn)全释,當(dāng)我們?cè)谑褂?code>mTextView都需要帶上
?
來訪問装处。
class MainActivity : AppCompatActivity() {
private var mTextView: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTextView = findViewById(R.id.tv_content)
mTextView?.text="hello world"
}
}
- 我們使用延遲初始化
lateinit
來讓編譯器繞過的警告。這樣我們就可以不用馬上初始化恨溜。
class MainActivity : AppCompatActivity() {
private lateinit var mTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTextView = findViewById(R.id.tv_content)
mTextView.text = "hello world"
}
}
雖然能夠處理上述的問題符衔。但明顯不夠。我們需要知道
lateinit
的更多細(xì)節(jié)糟袁,能夠在項(xiàng)目中更好的使用它判族。
lateinit 注意事項(xiàng)
- lateinit 只能作用于可變的屬性上,也就是說你不能作用在
val
的屬性上项戴。 - 當(dāng)你使用
lateinit
修飾后形帮,之后一定要自行初始化。編譯器已經(jīng)無法推斷你是否初始化。
private lateinit var mTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//雖然你沒有初始化辩撑,編譯器不會(huì)存在任何警告
mTextView.text = "hello world"
}
有沒有辦法處理上述的問題呢界斜?
Kotlin
中還提供了一個(gè)lazy
來處理延遲操作。具體使用如下
class MainActivity : AppCompatActivity() {
private val mTextView: TextView by lazy {
print("i am init")
findViewById<TextView>(R.id.tv_content)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTextView.text = "hello world"
}
}
當(dāng)你真正需要用到mTextView
的時(shí)候, lazy
后面的block
才會(huì)執(zhí)行合冀。假設(shè)你并沒有使用到mTextView
各薇。它是不會(huì)被初始化的。當(dāng)然上述lateinit
存在的注意事項(xiàng)君躺,在lazy
被完全忽視了峭判。而且它只會(huì)被初始化一次。
到這里棕叫,我們已經(jīng)大概知道了
lazy
和lateinit
的使用林螃,如果你想深究一下里面的原理的話“称可以繼續(xù)閱讀下去疗认。
首先是 lateinit
,當(dāng)我們將kotlin
代碼轉(zhuǎn)化成java
代碼的時(shí)候,你會(huì)發(fā)現(xiàn)其實(shí)lateinit
只是繞過編譯器的小把戲~
public final class MainActivity extends AppCompatActivity {
private TextView mTextView;
private HashMap _$_findViewCache;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(-1300009);
View var10001 = this.findViewById(-1000066);
Intrinsics.checkExpressionValueIsNotNull(var10001, "findViewById(R.id.tv_content)");
this.mTextView = (TextView)var10001;
TextView var10000 = this.mTextView;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("mTextView");
}
var10000.setText((CharSequence)"hello world");
}
...
}
接下來是lazy
,當(dāng)我們跟進(jìn)lazy
源代碼的時(shí)候伏钠,會(huì)發(fā)現(xiàn)一個(gè)這樣的函數(shù)
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
它將我們的block
根據(jù)SynchronizedLazyImpl
包裝成Lazy<T>
類型的參數(shù)横漏。 我們繼續(xù)看SynchronizedLazyImpl
實(shí)現(xiàn)
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
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
SynchronizedLazyImpl
里面的默認(rèn)狀態(tài)是UNINITIALIZED_VALUE
,當(dāng)我們調(diào)用了對(duì)象的屬性或則方法的時(shí)候,SynchronizedLazyImpl
才會(huì)調(diào)用block
里面的邏輯熟掂。然后改變_value
狀態(tài)绊茧。從而達(dá)到懶加載的作用。