首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >又被 Kotlin 语法糖坑惨的一天

又被 Kotlin 语法糖坑惨的一天

作者头像
codelang
发布2022-11-30 14:40:31
4210
发布2022-11-30 14:40:31
举报
文章被收录于专栏:codelangcodelang

源起是同事的一次反馈,在提测期间报了一个 Kotlin.Lazy 的空指针异常,Lazy 的定义如下:

class TestA{
    ...
    val xxxx:Service? by lazy{
         xxxService()   
    }
    ...
}

看起来很平常的 by lazy 为何会报空指针?在深入 lazy 源码查看的时候,并未发现任何可疑点,由于当时的代码逻辑涉及到并发调用,也查看了 by lazy 的初始化,默认实现是 SynchronizedLazyImpl,已经做了线程安全操作。

为了避免太多代码的干扰,我们将涉及到 by lazy 使用的地方都拷贝到了一个 Test 类中,然后通过 Decompile 反编译成 Java 代码来查看是否是 kotlin 的问题。

Kotlin 代码如下:

class TestA {

    init {
        ....
        initView()
    }

    private fun initView() {
        // 调用 Service 方法
        service?.getName()
    }

    private val service: AService? by lazy {
        AService()
    }
}

反编译后的 Java 代码:

public final class TestA {
   private final Lazy service$delegate;

   private final void initView() {
      // 1、获取 service 实例
      AService var10000 = this.getService();
      if (var10000 != null) {
         var10000.getName();
      }
   }

   private final AService getService() {
      Lazy var1 = this.service$delegate;
      Object var3 = null;
      // 2、调用 Lazy 的 getValue 方法
     .return (AService)var1.getValue();
   }

   public TestA() {
      this.initView();
      // 3、初始化 Lazy 实例
     .this.service$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }
}

通过代码的反编译立马查到问题:

  1. 在 TestA 的构造方法中,先执行 initView 方法获取 AService 的实例
  2. 但 getService 方法中的 Lazy 还没有初始化,却直接调用了 getValue 方法触发空指针异常
  3. 在 initView 结束之后再做 Lazy 的初始化,这时候已经晚了,异常已经出现了

那如何解决这问题呢?只需将 by lazy 提到了 init 代码块的前面,如下:

class TestA {
    private val service: AService? by lazy {
        AService()
    }

    init {
        initView()
    }
    ...
}

反编译结果:

public final class TestA {
   private final Lazy service$delegate;
   ....
   public TestA() {
      // 1、初始化 Lazy 实例
     .this.service$delegate = LazyKt.lazy((Function0)null.INSTANCE);
      // 2、再调用 getService 方法
     .this.initView();
   }
}
  1. 构造终于是先初始化 Lazy 对象
  2. 再调用 initView 方法,这时候方法内的 Lazy.getValue 就能被正常调用了

是不是有点违背常识?为什么在方法里调用一个变量还会涉及到变量放置的位置,Kotlin 这高级语法糖恐怕连 C 都不如吧(嘲笑一番,哈哈)。

那 Kotlin 真的没有对其做语法检查吗?其实是有的,我改变下代码给大家看下:

IDE 会提示当前 service 未初始化,「但该提示仅限在 init 代码块中调用 lazy 的时候提示,如果在 init 中调用一个中间方法,然后再从中间方法调用 lazy,该提示校验将会失效」

又被 Kotlin 语法糖坑惨的一天!!!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 扣浪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档