最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

聊聊Kotlin?中?lateinit?和?lazy?的原理區(qū)別

 更新時間:2022年07月26日 11:17:31   作者:TechMerger  
使用 Kotlin 進(jìn)行開發(fā),對于 latelinit 和 lazy 肯定不陌生。但其原理上的區(qū)別,可能鮮少了解過,借著本篇文章普及下這方面的知識,感興趣的朋友一起看看吧

使用 Kotlin 進(jìn)行開發(fā),對于 latelinit 和 lazy 肯定不陌生。但其原理上的區(qū)別,可能鮮少了解過,借著本篇文章普及下這方面的知識。

lateinit

用法

非空類型可以使用 lateinit 關(guān)鍵字達(dá)到延遲初始化。

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

如果在使用前沒有初始化的話會發(fā)生如下 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 進(jìn)行判斷。

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

當(dāng) 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 則拋出對應(yīng)的 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()
 }

在反編譯之后發(fā)現(xiàn)變量多了 @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 的話進(jìn)行反編譯之后可以發(fā)現(xiàn)就是在使用前進(jìn)行了 null 檢查,為空直接執(zhí)行預(yù)設(shè)邏輯,反之才進(jìn)行變量的使用。

 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 類似,但使用場景不同。其是用于懶加載,即初始化方式已確定,只是在使用的時候執(zhí)行。而且修飾的只是能是 val 常量。

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

lazy 修飾的變量可以直接使用,不用擔(dān)心 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 創(chuàng)建實(shí)例的時候,實(shí)際分配給 lazy 變量的是 Lazy 接口類型,并非 T 類型,變量會在 Lazy 中以 value 暫存,當(dāng)使用該變量的時候會獲取 Lazy 的 value 屬性。

Lazy 接口的默認(rèn) mode 是 LazyThreadSafetyMode.SYNCHRONIZED,其默認(rèn)實(shí)現(xiàn)是 SynchronizedLazyImpl,該實(shí)現(xiàn)中 _value 屬性為實(shí)際的值,用 volatile 修飾。

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

  • 已經(jīng)初始化過的話,轉(zhuǎn)換為 T 類型后返回

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

    • 已經(jīng)初始化過的話,轉(zhuǎn)換為 T 類型后返回
    • 反之,執(zhí)行用于初始化的函數(shù) 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 在上述默認(rèn)的 SYNCHRONIZED mode 下還可以指定內(nèi)部同步的 lock 對象。

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

lazy 還可以指定其他 mode,比如 PUBLICATION,內(nèi)部采用不同于 synchronizedCAS 機(jī)制。

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

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

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

the end

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

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

  • 在使用前需要初始化
  • 如果使用時沒有初始化內(nèi)部會拋出 UninitializedPropertyAccess Exception
  • 可配合 isInitialized 在使用前進(jìn)行檢查

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

  • 定義的時候已經(jīng)明確了 initializer 函數(shù)體
  • 使用的時候才進(jìn)行初始化,內(nèi)部默認(rèn)通過同步鎖和雙重校驗(yàn)的方式返回持有的實(shí)例
  • 還支持設(shè)置 lock 對象和其他實(shí)現(xiàn) mode

references

到此這篇關(guān)于聊聊Kotlin 中 lateinit 和 lazy 的區(qū)別解析的文章就介紹到這了,更多相關(guān)Kotlin 中 lateinit 和 lazy區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring的@Bean和@Autowired組合使用詳解

    Spring的@Bean和@Autowired組合使用詳解

    這篇文章主要介紹了Spring的@Bean和@Autowired組合使用詳解,Spring的@Bean注解用于告訴方法,產(chǎn)生一個Bean對象,然后這個Bean對象交給Spring管理,產(chǎn)生這個Bean對象的方法Spring只會調(diào)用一次隨后會將這個Bean對象放在自己的IOC容器,需要的朋友可以參考下
    2024-01-01
  • java中request對象各種方法的使用實(shí)例分析

    java中request對象各種方法的使用實(shí)例分析

    這篇文章主要介紹了java中request對象各種方法的使用,結(jié)合完整實(shí)例形式較為詳細(xì)的分析了request對象的功能及其常用方法的使用技巧,需要的朋友可以參考下
    2015-12-12
  • Java并發(fā)包線程池ThreadPoolExecutor的實(shí)現(xiàn)

    Java并發(fā)包線程池ThreadPoolExecutor的實(shí)現(xiàn)

    本文主要介紹了Java并發(fā)包線程池ThreadPoolExecutor的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • JDK1.7 之java.nio.file.Files 讀取文件僅需一行代碼實(shí)現(xiàn)

    JDK1.7 之java.nio.file.Files 讀取文件僅需一行代碼實(shí)現(xiàn)

    下面小編就為大家分享一篇JDK1.7 之java.nio.file.Files 讀取文件僅需一行代碼實(shí)現(xiàn),具有很好的參考價值,希望對大家有所幫助
    2017-11-11
  • java使用UDP實(shí)現(xiàn)多人聊天功能

    java使用UDP實(shí)現(xiàn)多人聊天功能

    這篇文章主要為大家詳細(xì)介紹了java使用UDP實(shí)現(xiàn)多人聊天功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringMVC中Model和ModelAndView的EL表達(dá)式取值方法

    SpringMVC中Model和ModelAndView的EL表達(dá)式取值方法

    下面小編就為大家分享一篇SpringMVC中Model和ModelAndView的EL表達(dá)式取值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • 淺談Java之終止繼承:Final類和Fianl方法

    淺談Java之終止繼承:Final類和Fianl方法

    這篇文章主要介紹了Java之終止繼承:Final類和Fianl方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例

    SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例

    這篇文章主要介紹了SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的實(shí)戰(zhàn)案例,文中通過示例代碼和圖文展示介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2024-01-01
  • 關(guān)于MyBatis的foreach標(biāo)簽常用方法

    關(guān)于MyBatis的foreach標(biāo)簽常用方法

    這篇文章主要介紹了關(guān)于MyBatis的foreach標(biāo)簽常用方法,foreach 標(biāo)簽可以用來遍歷數(shù)組、列表和 Map 等集合參數(shù),實(shí)現(xiàn)批量操作或一些簡單 SQL 操作,需要的朋友可以參考下
    2023-05-05
  • 教你在?Java?中實(shí)現(xiàn)?Dijkstra?最短路算法的方法

    教你在?Java?中實(shí)現(xiàn)?Dijkstra?最短路算法的方法

    這篇文章主要教你在?Java?中實(shí)現(xiàn)?Dijkstra?最短路算法的方法,在實(shí)現(xiàn)最短路算法之前需要先實(shí)現(xiàn)帶權(quán)有向圖,文章中給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04

最新評論

兰坪| 合作市| 琼结县| 鲁甸县| 贵德县| 乌拉特中旗| 耿马| 白水县| 棋牌| 望城县| 博野县| 九江市| 万源市| 郯城县| 广安市| 桃园县| 通榆县| 麻阳| 扶沟县| 长宁县| 南平市| 青田县| 利川市| 涞源县| 石城县| 曲阜市| 策勒县| 乌拉特前旗| 上高县| 缙云县| 囊谦县| 江川县| 玉门市| 古丈县| 赞皇县| 南华县| 舒城县| 新营市| 招远市| 九台市| 珠海市|