Kotlin 核心编程 (47):面向对象 3.1.3

阅读数:19 2019 年 12 月 25 日 15:35

Kotlin核心编程(47):面向对象 3.1.3

(主从构造方法)

内容简介
本书不是一本简单介绍 Kotlin 语法应用的图书,而是一部专注于帮助读者深入理解 Kotlin 的设计理念,指导读者实现 Kotlin 高层次开发的实战型著作。书中深入介绍了 Kotlin 的核心语言特性、设计模式、函数式编程、异步开发等内容,并以 Android 和 Web 两个平台为背景,演示了 Kotlin 的实战应用。
全书共 13 章,分为 4 个部分:
热身篇—Kotlin 基础(第 1~2 章),简单介绍了 Kotlin 设计哲学、生态及基础语法,其中包括 Kotlin 与 Scala、Java 之间的关联与对比,以及 Kotlin 的类型声明的特殊性、val 和 var 的使用、高阶函数的使用、面向表达式编程的使用、字符串的定义与操作等内容;
下水篇—Kotlin 核心(第 3~8 章),深入介绍了面向对象、代数数据类型、模式匹配、类型系统、Lambda、集合、多态、扩展、元编程等 Kotlin 开发核心知识,这是本书的重点,其中涉及很多开发者特别关心的问题,比如多继承问题、模式匹配问题、用代数数据类型抽象业务问题、泛型问题、反射问题等。
潜入篇—Kotlin 探索(第 9~11 章),探索 Kotlin 在设计模式、函数式编程、异步和并发等编程领域的应用,其中包括对 4 大类设计模式、Typeclass 实现、函数式通用结构设计、类型替代异常处理、共享资源控制、CQRS 架构等重点内容的深入剖析;
遨游篇—Kotlin 实战(第 12~13 章),着重演示了 Kotlin 在 Android 和 Web 平台的实战案例,其中涉及架构方式、单向数据流模型、解耦视图导航、响应式编程、Spring 5 响应式框架和编程等内容。

我们似乎遗漏了另一些常见的情况。有些时候,我们可能需要从一个特殊的数据中来获取构造类的参数值,这时候如果可以定义一个额外的构造方法,接收一个自定义的参数会显得特别方便。

同样以鸟为例,先把之前的 Bird 类简化为:

复制代码
class Bird(age: Int) {
val age: Int
init {
this.age = age
}
}

假设当前我们知道鸟的生日,希望可以通过生日来得到鸟的年龄,然后创建一个 Bird 类对象。如何实现?

第 1 种方案是在别处定义一个工厂方法,如:

复制代码
import org.joda.time.DateTime
fun Bird(birth: DateTime) = Bird(getAgeByBirth(birth))

应该在哪里声明这个工厂方法呢?这种方式的缺点在于,Bird 方法与 Bird 类在代码层面的分离显得不够直观。

一种改进方案是在 Bird 类的伴生对象中定义 Bird 方法。我们会在后续的节中介绍这种技术。

显然我们可以像 Java 那样新增一个构造方法来解决这个问题。其实 Kotlin 也支持多构造方法的语法,然而与 Java 的区别在于,它在多构造方法之间建立了“主从”的关系。现在我们来用 Kotlin 中的多构造方法实现这个例子:

复制代码
import org.joda.time.DateTime
class Bird(age: Int) {
val age: Int
{1}
init {
this.age = age
}
constructor(birth: DateTime) : this(getAgeByBirth(birth)) {
...
}
}

来看看这个新的构造方法是如何运作的:

  • 通过 constructor 方法定义了一个新的构造方法,它被称为从构造方法。相应地,我们熟悉的在类外部定义的构造方法被称为主构造方法。每个类可最多存在一个主构造方法和多个从构造方法,如果主构造方法存在注解或可见性修饰符,也必须像从构造方法一样加上 constructor 关键字,如:

    复制代码
    internal public Bird @inject constructor(age: Int) { ... }
  • 每个从构造方法由两部分组成。一部分是对其他构造方法的委托,另一部分是由花括号包裹的代码块。执行顺序上会先执行委托的方法,然后执行自身代码块的逻辑。

通过 this 关键字来调用要委托的构造方法。如果一个类存在主构造方法,那么每个从构造方法都要直接或间接地委托给它。比如,可以把从构造方法 A 委托给从构造方法 B,再将从构造方法 B 委托给主构造方法。举个例子:

复制代码
import org.joda.time.DateTime import org.joda.time.Years
class Bird(age: Int) { val age: Int init { this.age = age } constructor(timestamp: Long): this(DateTime(timestamp)) // 构造函数 A constructor(birth: DateTime): this(getAgeByBirth(birth)) // 构造函数 B }
fun getAgeByBirth(birth: DateTime): Int { return Years.yearsBetween(birth, DateTime.now()).years }

现在你应该对 Kotlin 中的主从构造方法有了一定的了解了。其实,从构造方法的设计除了解决我们以上的场景之外,还有一个很大的作用就是可以对某些 Java 的类库进行更好地扩展,因为我们经常要基于第三方 Java 库中的类,扩展自定义的构造方法。如果你从事过 Android 开发肯定了解,典型的例子就是定制业务中特殊的 View 类。比如以下的代码:

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

可以看出,利用从构造方法,我们就能使用不同参数来初始化第三方类库中的类了。

Kotlin核心编程(47):面向对象 3.1.3

购书地址 https://item.jd.com/12519581.html?dist=jd

评论

发布