又被 Kotlin 语法糖坑惨的一天

源起是同事的一次反馈,在提测期间报了一个 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 语法糖坑惨的一天!!!

作者有个置顶,一起放上学习~

 

转自:又被 Kotlin 语法糖坑惨的一天

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值