写点什么

Kotlin 类声明和构造器 (constructor)

作者:子不语Any
  • 2022-11-28
    湖南
  • 本文字数:1829 字

    阅读完需:约 6 分钟

Kotlin类声明和构造器(constructor)

前言

本文主要巩固 Kotlin 构造器的知识点。

本文大纲

1. Java 和 Kotlin 构造器对比

Java 的构造器声明和方法声明没有太大区别,支持重载,唯一的限制是:必须调用父类构造器(如果父类只有一个构造器而且是无参的,编译器会自动加上)。使用 Java 时,构造器并没有带来不便,很少人吐槽 Java 的构造器声明不合理,算是规规矩矩。现代编程语言却从构造器身上找到了优化空间,Kotlin 就是其中之一。


直接上代码对比 Kotlin 和 Java 的构造器声明的区别:


// Java 版public class RecordingBottomView extends ConstraintLayout implements View.OnClickListener {    ...
public RecordingBottomView(Context context) { super(context); init(context, null, 0); }
public RecordingBottomView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); }
public RecordingBottomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } ...}
复制代码


非常经典的代码。


对应的,Kotlin 版实现如下:


class RecordingBottomView(context: Context, attrs: AttributeSet?, defStyleAttr: Int):        ConstraintLayout(context, attrs, defStyleAttr),        View.OnClickListener {
constructor(context: Context): this(context, null, 0)
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)}
复制代码


在接触 Kotlin 这种构造器声明之前,我没有想过 Java 的构造器声明有什么缺点。但当接触 Kotlin 之后,就开始思考 Kotlin 为什么要这样设计构造器声明,以及 Java 构造器声明的不足之处:


  1. Java 构造器成员变量如果依赖构造参数,它们的声明和最终赋值是分离的,同一个成员变量的代码是低内聚的。而且像defaultValue这个参数,虽然有初始值,但在一定条件下,可能会被重新赋值。

  2. 所有的初始化代码都在一个函数中,很容易出现“超级函数”。不同成员变量的初始化代码大部分互相没有联系,但是却以先后顺序的形式耦合在同一个函数中,这是高耦合。

  3. Java 构造器允许重载,虽然设计规范提倡重载函数应最终调用同一个实现,以得到清晰的逻辑表达。但这不是强制性的。意味着可能会遇到多个构造器各自拥有自己的实现,这会加重问题 1,2 的严重性。


对应的,Kotlin 采用如下对策来一一解决这些问题:


  1. property 声明初始化时允许使用主构造器参数,变量声明和初始化代码都写在同一个地方,代码是高内聚的;

  2. 使用 let 闭包后,成员变量的所有的初始化代码都可以写在闭包内。不同的成员变量初始化代码相互独立,代码是低耦合的;

  3. 仅允许一个主构造器,其他构造器为从构造器,并约定从构造器必须调用主构造器,让主构造器去调用父构造器。


若 Kotlin 类没有声明主构造器,全部都是从构造器,则退化为 Java 构造器风格,没有调用主构造器的约束。这样设计一是为了 Java 转 Kotlin 代码时能兼容旧代码结构,不用重构也能直接转换为 Kotlin 代码;二也方便了 Java 转 Kotlin 自动化工具的实现。但属性 property 的初始化无法引用从构造器的入参,因为从构造器是可以有多个的,从调用上无法保证每个从构造器的每个参数都存在。

2. Kotlin 构造器的实现分析

上面看了 Kotlin 对 Java 构造器的优化,但 Java 采用这样的设计,是因为它忠实的反映了 JVM 的构造器实现。而 Kotlin 的构造器设计,并不符合 JVM 的实现。Kotlin 要最终在 JVM 上运行,必须在编译期处理,最终变回类似 Java 构造器的实现。


看一下 Kotlin 反编译后的字节码:


public RecordingBottomView(@NotNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {...property......init...}
public RecordingBottomView(@NotNull Context context) { Intrinsics.checkParameterIsNotNull(context, "context"); this(context, null, 0);}
public RecordingBottomView(@NotNull Context context, @Nullable AttributeSet attrs) { Intrinsics.checkParameterIsNotNull(context, "context"); this(context, attrs, 0);}
复制代码


可以看到,property 和 init 块初始化代码会被顺序的放入主构造器中,就是说代码是从上往下按顺序执行的。因此 Kotlin 的初始化代码不仅可以使用主构造器的参数,还可以使用比自己先初始化的 property 和 init 块

发布于: 刚刚阅读数: 4
用户头像

子不语Any

关注

If not now,when? 2022-09-17 加入

安卓攻城狮

评论

发布
暂无评论
Kotlin类声明和构造器(constructor)_android_子不语Any_InfoQ写作社区