Kotlin 类声明和构造器 (constructor)
前言
本文主要巩固 Kotlin 构造器的知识点。
本文大纲
1. Java 和 Kotlin 构造器对比
Java 的构造器声明和方法声明没有太大区别,支持重载,唯一的限制是:必须调用父类构造器(如果父类只有一个构造器而且是无参的,编译器会自动加上)。使用 Java 时,构造器并没有带来不便,很少人吐槽 Java 的构造器声明不合理,算是规规矩矩。现代编程语言却从构造器身上找到了优化空间,Kotlin 就是其中之一。
直接上代码对比 Kotlin 和 Java 的构造器声明的区别:
非常经典的代码。
对应的,Kotlin 版实现如下:
在接触 Kotlin 这种构造器声明之前,我没有想过 Java 的构造器声明有什么缺点。但当接触 Kotlin 之后,就开始思考 Kotlin 为什么要这样设计构造器声明,以及 Java 构造器声明的不足之处:
Java 构造器成员变量如果依赖构造参数,它们的声明和最终赋值是分离的,同一个成员变量的代码是低内聚的。而且像
defaultValue
这个参数,虽然有初始值,但在一定条件下,可能会被重新赋值。所有的初始化代码都在一个函数中,很容易出现“超级函数”。不同成员变量的初始化代码大部分互相没有联系,但是却以先后顺序的形式耦合在同一个函数中,这是高耦合。
Java 构造器允许重载,虽然设计规范提倡重载函数应最终调用同一个实现,以得到清晰的逻辑表达。但这不是强制性的。意味着可能会遇到多个构造器各自拥有自己的实现,这会加重问题 1,2 的严重性。
对应的,Kotlin 采用如下对策来一一解决这些问题:
property 声明初始化时允许使用主构造器参数,变量声明和初始化代码都写在同一个地方,代码是高内聚的;
使用 let 闭包后,成员变量的所有的初始化代码都可以写在闭包内。不同的成员变量初始化代码相互独立,代码是低耦合的;
仅允许一个主构造器,其他构造器为从构造器,并约定从构造器必须调用主构造器,让主构造器去调用父构造器。
若 Kotlin 类没有声明主构造器,全部都是从构造器,则退化为 Java 构造器风格,没有调用主构造器的约束。这样设计一是为了 Java 转 Kotlin 代码时能兼容旧代码结构,不用重构也能直接转换为 Kotlin 代码;二也方便了 Java 转 Kotlin 自动化工具的实现。但属性 property 的初始化无法引用从构造器的入参,因为从构造器是可以有多个的,从调用上无法保证每个从构造器的每个参数都存在。
2. Kotlin 构造器的实现分析
上面看了 Kotlin 对 Java 构造器的优化,但 Java 采用这样的设计,是因为它忠实的反映了 JVM 的构造器实现。而 Kotlin 的构造器设计,并不符合 JVM 的实现。Kotlin 要最终在 JVM 上运行,必须在编译期处理,最终变回类似 Java 构造器的实现。
看一下 Kotlin 反编译后的字节码:
可以看到,property 和 init 块初始化代码会被顺序的放入主构造器中,就是说代码是从上往下按顺序执行的。因此 Kotlin 的初始化代码不仅可以使用主构造器的参数,还可以使用比自己先初始化的 property 和 init 块
版权声明: 本文为 InfoQ 作者【子不语Any】的原创文章。
原文链接:【http://xie.infoq.cn/article/d11fd6d23675ed44371df6091】。文章转载请联系作者。
评论