Koltin中属性在声明的同时也要求要被初始化,否则会报错。例如以下代码:
-
private var name0: String //报错
-
private var name1: String = "xiaoming" //不报错
-
private var name2: String? = null //不报错
我并不想声明一个类型可空的对象,而且我也没办法在对象一声明的时候就为它初始化,那么这时就需要用到Kotlin提供的延迟初始化。
Kotlin中有两种延迟初始化的方式。一种是lateinit var,一种是by lazy。 lateinit 只用于变量 var,而 lazy 只用于常量 val
lateinit var
private lateinit var name: String
lateinit var只能用来修饰类属性,不能用来修饰局部变量,并且只能用来修饰对象,不能用来修饰基本类型(因为基本类型的属性在类加载后的准备阶段都会被初始化为默认值)。.lateinit不能用在可空的属性上和java的基本类型上
lateinit var的作用也比较简单,就是让编译期在检查时不要因为属性变量未被初始化而报错。
Kotlin相信当开发者显式使用lateinit var 关键字的时候,他一定也会在后面某个合理的时机将该属性对象初始化的
by lazy
by lazy本身是一种属性委托。属性委托的关键字是by
。by lazy 的写法如下:
-
//用于属性延迟初始化
-
val name: Int by lazy { 1 }
-
//用于局部变量延迟初始化
-
public fun foo() {
-
val bar by lazy { "hello" }
-
println(bar)
-
}
以下以name属性为代表来讲解by kazy的原理,局部变量的初始化也是一样的原理。
by lazy要求属性声明为val
,即不可变变量,在java中相当于被final
修饰。
这意味着该变量一旦初始化后就不允许再被修改值了(基本类型是值不能被修改,对象类型是引用不能被修改)。{}
内的操作就是返回唯一一次初始化的结果。
by lazy可以使用于类属性或者局部变量。
写一段最简单的代码分析by lazy的实现:
-
class TestCase {
-
private val name: Int by lazy { 1 }
-
fun printname() {
-
println(name)
-
}
-
}
lazy 应用于单例模式(if-null-then-init-else-return),而且当且仅当变量被第一次调用的时候,委托方法才会执行。lateinit 则用于只能生命周期流程中进行获取或者初始化的变量,比如 Android 的 onCreate()
lazy默认是线程安全的,你当然也可以关掉这个配置,只需要加个参数即可:
by lazy(LazyThreadSafetyMode.NONE)
如果你想要线程安全,使用 blockingLazy(): 它还是按照同样的方式工作,但保证了它的值只会在一个线程中计算,并且所有的线程都获取的同一个值。
class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> {
val prefs by lazy { context.getSharedPreferences("default", Context.MODE_PRIVATE) }
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return findPreference(name, default)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putPreference(name, value)
}
private fun <U> findPreference(name: String, default: U): U = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}
res as U
}
private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("This type can be saved into Preferences")
}.apply()
}
}
var aInt: Int by Preference(this, "aInt", 0)
fun whatever(){
println(aInt)//会从SharedPreference取这个数据
aInt = 9 //会将这个数据写入SharedPreference
}