当前位置:网站首页>Can you tell me the difference between lateinit and lazy in kotlin?
Can you tell me the difference between lateinit and lazy in kotlin?
2022-07-27 13:19:00 【TechMerger】

Use Kotlin Development , about latelinit and lazy It's not strange . But the difference in principle , Probably little known , With this article, popularize this knowledge .
lateinit
usage
Non empty types can use lateinit Keyword reached delayed initialization .
class InitTest() {
lateinit var name: String
public fun checkName(): Boolean = name.isNotEmpty()
}
If it is not initialized before use, the following will happen Exception.
AndroidRuntime: FATAL EXCEPTION: main
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
at com.example.tiramisu_demo.kotlin.InitTest.getName(InitTest.kt:4)
at com.example.tiramisu_demo.kotlin.InitTest.checkName(InitTest.kt:10)
at com.example.tiramisu_demo.MainActivity.testInit(MainActivity.kt:365)
at com.example.tiramisu_demo.MainActivity.onButtonClick(MainActivity.kt:371)
...
To prevent the above Exception, It can be passed before use ::xxx.isInitialized Judge .
class InitTest() {
lateinit var name: String
fun checkName(): Boolean {
return if (::name.isInitialized) {
name.isNotEmpty()
} else {
false
}
}
}
Init: testInit():false
When name After initialization, it can be used normally .
class InitTest() {
lateinit var name: String
fun injectName(name: String) {
this.name = name
}
fun checkName(): Boolean {
return if (::name.isInitialized) {
name.isNotEmpty()
} else {
false
}
}
}
Init: testInit():true
principle
After decompiling, you can see that the variable does not @NotNull annotation , When you use it, you should check Is it null.
public final class InitTest {
public String name;
@NotNull
public final String getName() {
String var10000 = this.name;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
}
return var10000;
}
public final boolean checkName() {
String var10000 = this.name;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
}
CharSequence var1 = (CharSequence)var10000;
return var1.length() > 0;
}
}
null Then throw the corresponding UninitializedPropertyAccessException.
public class Intrinsics {
public static void throwUninitializedPropertyAccessException(String propertyName) {
throwUninitializedProperty("lateinit property " + propertyName + " has not been initialized");
}
public static void throwUninitializedProperty(String message) {
throw sanitizeStackTrace(new UninitializedPropertyAccessException(message));
}
private static <T extends Throwable> T sanitizeStackTrace(T throwable) {
return sanitizeStackTrace(throwable, Intrinsics.class.getName());
}
static <T extends Throwable> T sanitizeStackTrace(T throwable, String classNameToDrop) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
int size = stackTrace.length;
int lastIntrinsic = -1;
for (int i = 0; i < size; i++) {
if (classNameToDrop.equals(stackTrace[i].getClassName())) {
lastIntrinsic = i;
}
}
StackTraceElement[] newStackTrace = Arrays.copyOfRange(stackTrace, lastIntrinsic + 1, size);
throwable.setStackTrace(newStackTrace);
return throwable;
}
}
public actual class UninitializedPropertyAccessException : RuntimeException {
...
}
If it is a variable, do not add lateinit Non empty type of , Initialization is required when defining .
class InitTest() {
val name: String = "test"
public fun checkName(): Boolean = name.isNotEmpty()
}
After decompiling, it is found that there are too many variables @NotNull annotation , Can be used directly .
public final class InitTest {
@NotNull
private String name = "test";
@NotNull
public final String getName() {
return this.name;
}
public final boolean checkName() {
CharSequence var1 = (CharSequence)this.name;
return var1.length() > 0;
}
}
::xxx.isInitialized After decompiling, you can find that it is done before use null Check , If it is empty, execute the preset logic directly , On the contrary, use variables .
public final class InitTest {
public String name;
...
public final boolean checkName() {
boolean var2;
if (((InitTest)this).name != null) {
String var10000 = this.name;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
}
CharSequence var1 = (CharSequence)var10000;
var2 = var1.length() > 0;
} else {
var2 = false;
}
return var2;
}
}
lazy
usage
lazy And lateinit similar , But the use scenario is different . It is used for lazy loading , That is, the initialization method has been determined , Only execute when using . And the decoration can only be val Constant .
class InitTest {
val name by lazy {
"test"
}
public fun checkName(): Boolean = name.isNotEmpty()
}
lazy Decorated variables can be used directly , Never mind NPE.
Init: testInit():true
principle
The above is lazy Most common usage , The decompiled code is as follows :
public final class InitTest {
@NotNull
private final Lazy name$delegate;
@NotNull
public final String getName() {
Lazy var1 = this.name$delegate;
return (String)var1.getValue();
}
public final boolean checkName() {
CharSequence var1 = (CharSequence)this.getName();
return var1.length() > 0;
}
public InitTest() {
this.name$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
Belongs to class When creating an instance , Actually assigned to lazy The of the variable is Lazy Interface type , Is not T type , The variable will be in Lazy China and Israel value The staging , When you use this variable, you will get Lazy Of value attribute .
Lazy Interface default mode yes LazyThreadSafetyMode.SYNCHRONIZED, The default implementation is SynchronizedLazyImpl, In this implementation _value Property is the actual value , use volatile modification .
value Through get() from _value Intermediate reading and writing ,get() Will check first _value Whether it has not been initialized
- If it has been initialized , Convert to T Type and return
- conversely , Perform synchronization method ( By default lock The object is impl example ), And check again whether it has been initialized :
- If it has been initialized , Convert to T Type and return
- conversely , Execute the function for initialization initializer, Its return value is stored in _value in , And back to
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
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)
}
Anyway, with Java In the double check lazy mode, the way to get a singleton is very similar .
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
lazy In the above default SYNCHRONIZED mode You can also specify the internal synchronization lock object .
val name by lazy(lock) {
"test"
}
lazy You can also specify other mode, such as PUBLICATION, Internal adoption is different from synchronized Of CAS Mechanism .
val name by lazy(LazyThreadSafetyMode.PUBLICATION) {
"test"
}
lazy You can also specify NONE mode, Thread unsafe .
val name by lazy(LazyThreadSafetyMode.NONE) {
"test"
}
the end
lateinit and lazy Are used to initialize the scene , There are some differences between usage and principle , Make a brief summary :
lateinit Used as initialization of non empty types :
- Initialization is required before use
- If it is not initialized during use, the internal will throw
UninitializedPropertyAccessException - Compatible
isInitializedCheck before use
lazy Delay initialization used as a variable :
- The definition is clear
initializerThe body of the function - Initialize only when using , The internal default is to return the held instance through synchronous lock and double check
- It also supports settings
lockObjects and other implementationsmode
references
边栏推荐
- How to get class objects
- @Simple understanding and use of conditionalonproperty
- Specify the add method of HashSet
- 正向预查和反向预查
- 【VSCode】SyntaxError: Cannot use import statement outside a module
- Connotative quotations
- How can the top 500 enterprises improve their R & D efficiency? Let's see what industry experts say!
- JS single thread understanding notes - Original
- From the perspective of it, the CIO of B2B industry talks about how to change from "cost center" to "growth center"?
- 元素的层级
猜你喜欢
随机推荐
Isolation level
Redis distributed online installation
记忆中的香河肉饼
[nuxt 3] (XII) project directory structure 2
Will saffron become a safe and effective natural therapy for patients with arthritis?
How to get class objects
关于 CMS 垃圾回收器,你真的懂了吗?
JS single thread understanding notes - Original
改变线程状态的方法
C语言犄角旮旯的知识之数组与函数
[node+ts] build node+typescript project
Do you really understand CMS garbage collector?
GAN:生成对抗网络 Generative Adversarial Networks
multi-table query
pg同步多张数据表至mysql 有办法简化配置吗?
@Simple use of conditional
Error: the source of the existing CacheManager is: urlconfigurationsource
Why do you need foreign keys?
Gan: generate adversarial networks
[expression calculation] double stack: general solution of expression calculation problem


![[cute new solution] Fibonacci sequence](/img/2d/64cdc8b7625ee7a81275ad25dc2b7a.png)






