前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NoArg 插件的 invokeInitializers 配置

NoArg 插件的 invokeInitializers 配置

作者头像
bennyhuo
发布2020-02-20 13:31:47
4830
发布2020-02-20 13:31:47
举报
文章被收录于专栏:BennyhuoBennyhuo

上周的时候 Kotlin 1.3 发布了,由于之前 RC 版本以及 KotlinConf 造势很多,所以正式版出来之后大家就改个版本号,把协程的 experimental 去掉,就差不多了。

上周微信群里有小伙伴说到 NoArg 插件生成的无参构造方法不会初始化类内部定义的属性,例如:

代码语言:javascript
复制
@Poko
data class DontDoThis(val requiredProperty: String,
                      val invalidDefaultValue: Int = 1000) {
    val wontBeInitialized by lazy {
        "HelloWorld"
    }

    val wontBeInitialized2 = 2
}

wontBeInitializedwontBeInitialized2 都不会被正常初始化。

  • 对于前者,我们知道编译器要为我们生成一个代理对象,我们访问它时,实际上是访问代理对象来获取对应的值,而代理对象因没有被初始化,导致访问前者时会出空指针。
  • 而后者,本身就是一个整型,不被初始化,访问时就是默认的 0。

前面我们已经有文章提到过这个现象,包括对于 Gson 反序列化数据类的时候出现的种种问题中,也有些与此有关。可以参考相关文章:

其实 NoArg 的配置还有一个叫 invokeInitializers 的家伙,你可以这么配置:

代码语言:javascript
复制
noArg{
    invokeInitializers = true
    annotation "com.bennyhuo.annos.Poko"
}

它是什么意思呢?对于前面的那个类,这个配置为 true 之后,生成的无参构造器就会大致相当于:

代码语言:javascript
复制
public DontDoThis(){
    super();
    wontBeInitialized$Delegate = new .... ;
    wontBeInitialized2 = 2;
}

而不加这个配置的话,就是这样:

代码语言:javascript
复制
public DontDoThis(){
    super();
}

看来这个配置还是很有用的。

过去我之前好几次看到它,并尝试配置,结果用 IntelliJ Kotlin 插件自带的 "Show Kotlin Bytecode" 看了之后,发现生成的构造器没有任何变化:

代码语言:javascript
复制
public <init>()V
  L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
  L1
    LOCALVARIABLE this Lkotlin/Unit; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

这让人相当的困惑,我一直以为这个配置没啥用。前几天在看 NoArg 插件的源码时看到这个配置,试了下还是有用的,估计是 "Show Kotlin Bytecode" 没有根据 Gradle 当中的配置来编译导致的吧,大家不用在意了,我们可以在 IntelliJ 里面下载其他看字节码的插件,其实是可以看到

代码语言:javascript
复制
// access flags 0x1
public <init>()V
   L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 6 L1
    ALOAD 0
    GETSTATIC com/bennyhuo/DontDoThis$wontBeInitialized$2.INSTANCE : Lcom/bennyhuo/DontDoThis$wontBeInitialized$2;
    CHECKCAST kotlin/jvm/functions/Function0
    INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
    PUTFIELD com/bennyhuo/DontDoThis.wontBeInitialized$delegate : Lkotlin/Lazy;
   L2
    LINENUMBER 10 L2
    ALOAD 0
    ICONST_2
    PUTFIELD com/bennyhuo/DontDoThis.wontBeInitialized2 : I
    RETURN
   L3
    LOCALVARIABLE this Lkotlin/Unit; L0 L3 0
    MAXSTACK = 2
    MAXLOCALS = 1

我们通过 Bytecode Viewer 的功能看到生成的无参构造方法的字节码其实是这样的,其中明显有对类内部的属性初始化的操作。

既然这个配置这么有用,为什么 Kotlin 官方把它默认关闭了?大约是因为 1.1.3 这个版本刚刚带上这个功能,当时因为有一些小问题,大家抱怨升级之后导致代码无法编译通过,影响太大,后来尽管问题已经在 1.1.3-2 修复,但这个可能影响程序结果的配置还是关掉了,这也是为了稳定性考虑,如果大家有明确的需要,还是自己手动打开吧。


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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档