在一些情况下,我们可能希望某些属性延迟加载,即在我们正在需要的时候才对它赋值;亦或者我们希望可以随时监听属性值的变化;又或者是多次调用该属性时,只对该属性赋值一次,既第一次赋值完就ok了,在上述这些场景中,代理属性就可以发挥作用了.
- lazy properties
它包含一个lambda,当第一次执行 getValue 的时候这个lambda会被调用,所以这个属性可以被延迟初始化。之后的调用都只会直接调用第一次返回的值;lazy 操作符是线程安全的 - observable properties
这个委托会帮我们监测我们希望观察的属性的变化.当被观察属性的 set 方法被调用的时候,它就会自动执行我们指定的lambda表达式。所以一旦该属性被赋了新的值,我们就会接收到被委托的属性、旧值和新值 - storing properties
属性的值会从一个map中获取value,属性的名字对应这个map中的key。
标准委托属性
Kotlin标准库为几种有用的委托提供了工厂方法
- lazy
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
// 输出:
computed!
Hello
Hello
调用了两次lazyValue,但是lambda:lazy{}只执行了一次,在第一执行的时候就把”Hello”赋值给lazyValue,当我们再次调用它的时候,他直接使用已存在的值.
例如:
class App : Application() {
val database: SQLiteOpenHelper by lazy {
MyDatabaseHelper(applicationContext)
}
override fun onCreate() {
super.onCreate()
val db = database.writableDatabase
}
}
database并没有被真正初始化,直到第一次调用 onCreate 时。因为在那之后,我们才确保applicationContext存在,并且已经准备好可以被使用了
lazy 操作符是线程安全的,因为它里边的任务是同步执行的,所以在多线程环境中,第一个进入同步代码块执行出的结果,就是lazy-property的值
lazy还有其他加载方法:
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
LazyThreadSafetyMode:
1. SYNCHRONIZED 同步执行
2. 内部的计算过程可能被多次访问,但内部的属性是一个Volatile修饰的属性,所以在多线程环境中,被第一次访问获取数据后,此后的其它线程都共享该值。
3. 未对多线程环境,做任何处理。
所以在多线程环境中使用,会造成:计算和返回值都可能处在多个线程中。
注:lazy属性,只能声明为 val的,即它是只能get的
- observable
使用方式属性 by Delegates.observable("init values") { lambda}
例:
interface TextChangedListener {
fun onTextChanged(newText: String)
}
class PrintingTextChangedListener : TextChangedListener {
override fun onTextChanged(newText: String) = println("Text is changed to: $newText")
}
class Data() {
var listener: TextChangedListener? = null
var text: String by Delegates.observable("init delegates") { property, oldValue, newValue ->
listener?.onTextChanged(newValue)
}
}
fun main(args: Array<String>) {
val data = Data()
data.listener = PrintingTextChangedListener()
data.text = "lijunjie"
data.text = "xxx"
}
// 输出:
Text is changed to: lijunjie
Text is changed to: xxx
property:属性(text)
oldValue:属性set之前的值
newValue:属性set之后的值
- Storing Properties in a Map
从Map中映射值到属性
例:
class Data(map: Map<String, Any?>) {
val userName: String by map
val age: Int by map
val avator: String by map
override fun toString(): String {
return "userName:$userName+age:$age+avator:$avator"
}
}
val data = Data(mapOf(
"userName" to "kotlin",
"age" to 18,
"avator" to "https://xxx.jpg"))
输出:
userName:kotlin+age:18+avator:https://xxx.jpg
- 自定义的委托
object DelegatesExt {
fun <T> notNullSingValue() = NotNullSingleValueVar<T>()
}
class NotNullSingleValueVar<T>: ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T =
value ?: throw IllegalStateException("${property.name} not initialized")
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = if (this.value == null) value
else throw IllegalStateException("${property.name} already initialized")
}
private var value: T? = null
}
在application中使用
companion object {
var instance: BaseApplication by DelegatesExt.notNullSingValue()
}
override fun onCreate() {
super.onCreate()
instance = this
}
除此之外还有另外一种实现方式既使用opeartor,例:用来进行SharedPreference数据存储
class Preference<T>(private val name: String, private val default: T) {
companion object {
lateinit var preferences: SharedPreferences
/**
* init Context
* @param context Context
*/
fun setContext(context: Context) {
preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
fun clear() {
preferences.edit().clear().apply()
}
}
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putPreference(name, value)
}
@Suppress("UNCHECKED_CAST")
private fun findPreference(name: String, default: T): T = with(preferences) {
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 T
}
@SuppressLint("CommitPrefEdits")
private fun putPreference(name: String, value: T) = with(preferences.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't be saved into Preferences")
}.apply()
}
}