kotlin小知识系列2-懒加载和代理

/ kotlin / 没有评论 / 136浏览

懒加载

kotlin懒加载有两种方式,lateinit var 和 lazy。

class LazyActivity : AppCompatActivity() {

    private lateinit var name: String

    private val city: String by lazy {
        "北京"
    }
    private val province by ProvinceLazy {
        "北京"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        name = "god"
    }
}

class ProvinceLazy(private val _initializer: () -> String) : Lazy<String> {

    private var _value: String? = null

    private val objLock = Object()

    override val value: String
        get() {
            if (_value == null) {
                synchronized(objLock) {
                    if (_value == null) {
                        _value = _initializer!!()
                    }
                }
            }

            return _value as String
        }

    override fun isInitialized() = _value != null

}

lateinit var懒加载初始化

lateinit var 懒加载方式,有几点需要注意

  1. 需要手动初始化,但是可以初始化多次,也就是该值可以改变
  2. 只能用于不可null的对象,不能为primitives(Int、Float之类)类型

同时我个人觉得也会带来一些好处

  1. 对于类似android的activity而言,某些在初始化的时候不能确定该值,只有在某些特定方法调用的时候才可以确定值的情况下,才能进行其初始化。常见onCreate初始化
  2. 对于第一点而言,可以有效的避免?号或者!!的大量使用影响阅读或者强迫证的人

这两点优势,我个人觉得还是很不错的。下面的例子

data class User(val name: String)

class lateinitActivity : AppCompatActivity() {
    private lateinit var user: User
    private var lastUser: User? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lastUser = User(intent.getStringExtra("name"))
        user = User(intent.getStringExtra("name"))

        Log.d("lateinitActivity", "user ${user.name}")
        //        Log.d("lateinitActivity", "lastuser ${lastUser.name}")//由于kotlin可空检查机制,不允许直接使用
        Log.d("lateinitActivity", "lastuser ${lastUser?.name}")
        Log.d("lateinitActivity", "lastuser ${lastUser!!.name}")
    }
}

lazy初始化方法

Lazy是一个接口,lazy是一个方法,返回一个Lazy对象。kotlin默认已经实现了常用的Lazy对象。lazy只能修饰val类型

lazy方法是真正的懒加载方法,它要使用者提供初始化过程,在第一次调用的时候会进行初始化。lazy所有的内容都Layz.kt里。

@kotlin.jvm.JvmVersion
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
        when (mode) {
            LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
            LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
            LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
        }

系统默认有三个Lazy的实现类。具体可以看下代码。

这里有个疑问,默认的lazy 方法只会进行一次的初始化,但是这个是系统实现的,如果自己实现Lazy接口呢?是否可以根据其他不同的变量返回不同的值呢?看下例子

class UserLazy : Lazy<User> {

    private val _user1 = User("god")
    private val _user2 = User("demon")
    private var _index = 0

    private val _users = arrayOf(_user1, _user2)

    override val value: User
        get() {
            return _users[_index++ % 2]
        }

    override fun isInitialized() = true

}

val user by UserLazy()

fun main(args: Array<String>) {
    println("$user")
    println("$user")
}

输入结果:
User(name=god)
User(name=demon)

~通过结果可以看出,是可以实现出不同的对象。所以说虽然user是val类型,但是可以改变其值的

代理ReadWriteProperty

属性代理,从抽象来说,就是hook属性的get和set方法,加入自身需要的逻辑和内容。一个很好的实践例子就是两个view的显示的状态跟随某个boolean变量切换。

省略部分代码

fun Activity.statusBinding(tViewID: Int, fViewID: Int): ReadWriteProperty<Any?, Boolean> {
    return StatusBinding(lazy { findViewById(tViewID) }, lazy { findViewById(fViewID) })
}

class StatusBinding(trueView: Lazy<View>, falseView: Lazy<View>) : ReadWriteProperty<Any?, Boolean> {

    private val tView by trueView
    private val fView by falseView

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
        if (value) {
            tView.visibility = View.VISIBLE
            fView.visibility = View.GONE
        } else {
            fView.visibility = View.VISIBLE
            tView.visibility = View.GONE
        }
    }

    override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean {
        return tView.visibility == View.VISIBLE
    }
}


---

class DelegateActivity : AppCompatActivity() {
    private var status:Boolean  by statusBinding(R.id.trueView, R.id.falseView)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin_delegate)
        change.setOnClickListener {
            status = !status  //会控制两个view的隐藏和现实
        }
    }
}

上面例子中,trueView和falseView的显示和隐藏会随着status变量的改变而改变。

整个代理看起来,有点像databinding,vue和react的状态机。例如还可以设置个String类型的content变量,绑定到一个textView中。

fun Activity.bindToTextView(textViewID: Int): ReadWriteProperty<Any?, String?> {
    return TextViewBinding(lazy { findViewById(textViewID) as TextView })
}
class TextViewBinding(viewLazy: Lazy<TextView>) : ReadWriteProperty<Any?, String?> {

    val view by viewLazy

    override fun getValue(thisRef: Any?, property: KProperty<*>): String? {
        return view.text?.toString()
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
        view.text = value
    }
}

class DelegateActivity : AppCompatActivity() {

    private var textContent: String? by bindToTextView(R.id.textView)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin_delegate)
        change.setOnClickListener {
            textContent = "代理设置" //会改变textview的现实内容
        }
    }
}

结束

kotlin的代理机制和懒加载机制是kotlin比较常用的技术,尤其是代理,应该多加的练习和实践,可以创造出很多有意思的东西