Kotlin 核心编程 (53):面向对象 3.3.2

阅读数:1 2019 年 12 月 28 日 22:25

Kotlin核心编程(53):面向对象 3.3.2

(接口实现多继承)

内容简介
本书不是一本简单介绍 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 响应式框架和编程等内容。

一个类实现多个接口相信你肯定不会陌生,这是 Java 经常干的事情。Kotlin 中的接口与 Java 很相似,但它除了可以定义带默认实现的方法之外,还可以声明抽象的属性。我们的第 1 个方案,就来看看如何用 Kotlin 中的接口来实现多继承。

复制代码
interface Flyer {
fun fly()
fun kind() = "flying animals"
}
interface Animal {
val name: String
fun eat()
fun kind() = "flying animals"
}
class Bird(override val name: String) : Flyer, Animal {
override fun eat() {
println("I can eat")
}
override fun fly() {
println("I can fly")
}
override fun kind() = super<Flyer>.kind()
}
fun main(args: Array<String>) {
val bird = Bird("sparrow")
println(bird.kind())
}
// 运行结果
flying animals

如你所见,Bird 类同时实现了 Flyer 和 Animal 两个接口,但由于它们都拥有默认的 kind 方法,同样会引起上面所说的钻石问题。而 Kotlin 提供了对应的方式来解决这个问题,那就是 super 关键字,我们可以利用它来指定继承哪个父接口的方法,比如上面代码中的 super<Flyer>.kind()。当然我们也可以主动实现方法,覆盖父接口的方法。如:

复制代码
override fun kind() = "a flying ${this.name}"

那么最终的执行结果就是:

复制代码
a flying sparrow

通过这个例子,我们再来分析下实现接口的相关语法:

1)在 Kotlin 中实现一个接口时,需要实现接口中没有默认实现的方法及未初始化的属性,若同时实现多个接口,而接口间又有相同方法名的默认实现时,则需要主动指定使用哪个接口的方法或者重写方法;

2)如果是默认的接口方法,你可以在实现类中通过“super<T>”这种方式调用它,其中 T 为拥有该方法的接口名;

3)在实现接口的属性和方法时,都必须带上 override 关键字,不能省略。

除此之外,你应该还注意到了,我们通过主构造方法参数的方式来实现 Animal 接口中的 name 属性。我们之前说过,通过 val 声明的构造方法参数,其实是在类内部定义了一个同名的属性,所以我们当然还可以把 name 的定义放在 Bird 类内部。

复制代码
class Bird(name: String) : Flyer, Animal {
override val name: String // override 不要忘记
init {
this.name = name
}
}

name 的赋值方式其实无关紧要。比如我们还可以用一个 getter 对它进行赋值。

复制代码
class Bird(chineseName: String) : Flyer, Animal {
override val name: String
get() = translate2EnglishName(chineseName)
}

getter 和 setter
对于 getter 和 setter 相信很多 Java 程序员再熟悉不过了,在 Java 中通过这种方式来对一个类的私有字段进行取值和赋值的操作,通常用 IDE 来帮我们自动生成这些方法。但是在很多时候你会发现这种语法真是不堪入目。而 Kotlin 类不存在字段,只有属性,它同样需要为每个属性生成 getter 和 setter 方法。但 Kotlin 的原则是简洁明了的,既然都要做,那么为何我不幕后就帮你做好了呢?所以你在声明一个类的属性时,要知道背后 Kotlin 编译器也帮你生成了 getter 和 setter 方法。当然你也可以主动声明这两个方法来实现一些特殊的逻辑。还有以下两点需要注意:
1)用 val 声明的属性将只有 getter 方法,因为它不可修改;而用 var 修饰的属性将同时拥有 getter 和 setter 方法。
2)用 private 修饰的属性编译器将会省略 getter 和 setter 方法,因为在类外部已经无法访问它了,这两个方法的存在也就没有意义了。

总的来说,用接口模拟实现多继承是我们最常用的方式。但它有时在语义上依旧并不是很明确。下面我们就来看一种更灵活的方式,它能更完整地解决多继承问题。

Kotlin核心编程(53):面向对象 3.3.2

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

评论

发布