一、lateinit
1.lateinit的使用
由于kotlin有严格的语法要求变量需要声明是否可以为null,但由于在实际的业务场景中,这个变量必须在某些时候才能做初始化操作,并且这个变量肯定不为null,如果为null,就是逻辑有问题了。这个时候可以使用lateinit来修饰这个变量。如果没有初始化就使用这个变量,那么就会抛出异常。
class LateInitExample {lateinit var value:String
}fun main() {val example = LateInitExample()// 如果没有赋值就使用,直接抛出异常。example.value = "lateinit example"println("${example.value}")
}
2.lateinit的具体实现
public class LateInitExample {private String value;public String getValue() {// 如果没有初始化过,就抛出异常if (value == null){throw new RuntimeException("lateinit property value has not been initialized");}return value;}public void setValue(String value) {// 这里要做非null检查this.value = value;}
}
我们知道使用kotlin的属性其实是在调用get和set方法,lateinit关键字其实就是对get和set方法做了一些操作。
注意lateinit不能修饰基本类型。
二、by lazy
by 和 lazy要单独拿出来看,不能当做一个整体来看。
by:这里涉及到了kotlin的委托中委托属性。
lazy:一个kotlin的函数
1.属性委托
简单通俗理解就是这个变量的get,set都是委托给了另外一个类来去操作。
如果是var变量,必须要有getValue和setValue2个方法,val变量不需要setValue方法。
// 委托的类
class Delegate {operator fun getValue(thisRef: Any?, property: KProperty<*>): String {return "$thisRef, 这里委托了 ${property.name} 属性"}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {println("$thisRef 的 ${property.name} 属性赋值为 $value")}
}
注意别和kotlin的get和set方法混淆了!!!!
语法是: val/var <属性名>: <类型> by <表达式>
Kotlin 标准库为几种有用的委托提供了工厂方法,延迟属性 Lazy就是其中之一。
https://www.runoob.com/kotlin/kotlin-delegated.html
2.lazy的实现
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)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 instanceprivate val lock = lock ?: thisoverride val value: Tget() {val _v1 = _valueif (_v1 !== UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")return _v1 as T}return synchronized(lock) {val _v2 = _valueif (_v2 !== UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T)} else {val typedValue = initializer!!()_value = typedValueinitializer = nulltypedValue}}}override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUEoverride fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."private fun writeReplace(): Any = InitializedLazyImpl(value)
}
看到这里,相信大家只剩下一个疑惑了,为什么没有getValue方法,不是说by的实现需要getValue方法吗?这里,kotlin使用了扩展函数来做。
// 这里返回value,就会执行实现类的override value get了。
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
三、总结
从本质上来说,lateinit和by lazy的区别是体现在内存上:
lateinit修饰的属性,会在内存中创建,只不过没有赋值
by lazy修饰的属性,只有在使用的时候才会在内存中创建