中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Kotlin中lateinit和lazy的原理區別是什么

發布時間:2022-07-26 18:01:15 來源:億速云 閱讀:160 作者:iii 欄目:開發技術

這篇“Kotlin中lateinit和lazy的原理區別是什么”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Kotlin中lateinit和lazy的原理區別是什么”文章吧。

    lateinit

    用法

    非空類型可以使用 lateinit 關鍵字達到延遲初始化。

     class InitTest() {
         lateinit var name: String
     
         public fun checkName(): Boolean = name.isNotEmpty()
     }

    如果在使用前沒有初始化的話會發生如下 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)
             ...

    為防止上述的 Exception,可以在使用前通過 ::xxx.isInitialized 進行判斷。

     class InitTest() {
         lateinit var name: String
     
         fun checkName(): Boolean {
             return if (::name.isInitialized) {
                 name.isNotEmpty()
             } else {
                 false
             }
         }
     }
     Init: testInit():false

    當 name 初始化過之后使用亦可正常。

     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

    原理

    反編譯之后可以看到該變量沒有 @NotNull 注解,使用的時候要 check 是否為 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 則拋出對應的 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 {
         ...
     }

    如果是變量是不加 lateinit 的非空類型,定義的時候即需要初始化。

     class InitTest() {
         val name: String = "test"
     
         public fun checkName(): Boolean = name.isNotEmpty()
     }

    在反編譯之后發現變量多了 @NotNull 注解,可直接使用。

     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 的話進行反編譯之后可以發現就是在使用前進行了 null 檢查,為空直接執行預設邏輯,反之才進行變量的使用。

     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

    用法

    lazy 的命名和 lateinit 類似,但使用場景不同。其是用于懶加載,即初始化方式已確定,只是在使用的時候執行。而且修飾的只是能是 val 常量。

     class InitTest {
         val name by lazy {
             "test"
         }
         
         public fun checkName(): Boolean = name.isNotEmpty()
     }

    lazy 修飾的變量可以直接使用,不用擔心 NPE。

     Init: testInit():true

    原理

    上述是 lazy 最常見的用法,反編譯之后的代碼如下:

     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);
        }
     }

    所屬 class 創建實例的時候,實際分配給 lazy 變量的是 Lazy 接口類型,并非 T 類型,變量會在 Lazy 中以 value 暫存,當使用該變量的時候會獲取 Lazy 的 value 屬性。

    Lazy 接口的默認 mode 是 LazyThreadSafetyMode.SYNCHRONIZED,其默認實現是 SynchronizedLazyImpl,該實現中 _value 屬性為實際的值,用 volatile 修飾。

    value 則通過 get() 從 _value 中讀寫,get() 將先檢查 _value 是否尚未初始化

    • 已經初始化過的話,轉換為 T 類型后返回

    • 反之,執行同步方法(默認情況下 lock 對象為 impl 實例),并再次檢查是否已經初始化:

      • 已經初始化過的話,轉換為 T 類型后返回

      • 反之,執行用于初始化的函數 initializer,其返回值存放在 _value 中,并返回

     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)
     }

    總之跟 Java 里雙重檢查懶漢模式獲取單例的寫法非常類似。

     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 在上述默認的 SYNCHRONIZED mode 下還可以指定內部同步的 lock 對象。

         val name by lazy(lock) {
             "test"
         }

    lazy 還可以指定其他 mode,比如 PUBLICATION,內部采用不同于 synchronizedCAS 機制。

         val name by lazy(LazyThreadSafetyMode.PUBLICATION) {
             "test"
         }

    lazy 還可以指定 NONE mode,線程不安全。

         val name by lazy(LazyThreadSafetyMode.NONE) {
             "test"
         }

    the end

    lateinit 和 lazy 都是用于初始化場景,用法和原理有些區別,做個簡單總結:

    lateinit 用作非空類型的初始化:

    • 在使用前需要初始化

    • 如果使用時沒有初始化內部會拋出 UninitializedPropertyAccess Exception

    • 可配合 isInitialized 在使用前進行檢查

    lazy 用作變量的延遲初始化:

    • 定義的時候已經明確了 initializer 函數體

    • 使用的時候才進行初始化,內部默認通過同步鎖和雙重校驗的方式返回持有的實例

    • 還支持設置 lock 對象和其他實現 mode

    以上就是關于“Kotlin中lateinit和lazy的原理區別是什么”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    普兰县| 依安县| 河源市| 正定县| 武安市| 吴忠市| 鹤庆县| 曲阜市| 桃源县| 乌兰县| 武川县| 潼南县| 旬邑县| 宝应县| 莱芜市| 大关县| 治多县| 沙雅县| 锡林浩特市| 龙游县| 临泉县| 嘉鱼县| 东辽县| 虎林市| 石楼县| 永登县| 华宁县| 泸溪县| 伊春市| 剑阁县| 瑞安市| 昌宁县| 西乌珠穆沁旗| 萝北县| 游戏| 永州市| 辽中县| 拜城县| 北京市| 南乐县| 慈利县|