Kotlin 核心编程 (45):面向对象 3.1.1

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

Kotlin核心编程(45):面向对象 3.1.1

(Kotlin 中的类及接口)

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

对象是什么?我们肯定再熟悉不过了,任何可以描述的事物都可以看作对象。我们以鸟为例,来分析它的组成。

  • 状态:形状、颜色等部件可以看作鸟的静态属性,大小、年龄等可以看作鸟的动态属性,对象的状态就是由这些属性来表现的。
  • 行为:飞行、进食、鸣叫等动作可以看作鸟的行为。
  1. Kotlin 中的类

对象是由状态和行为组成的,我们可以通过它们描述一个事物。下面我们就用 Kotlin 来抽象一个 Bird 类:

复制代码
// Kotlin 中的一个类
class Bird {
val weight: Double = 500.0
val color: String = "blue"
val age: Int = 1
fun fly() {} // 全局可见
}

是不是一点也不陌生?我们依然可以使用熟悉的 class 结构体来声明一个类。但是,Kotlin 中的类显然也存在很多不同。作为对照,我们把上述代码反编译成 Java 的版本,然后分析它们具体的差异。

复制代码
public final class Bird {
private final double weight = 500.0D;
@NotNull
private final String color = "blue";
private final int age = 1;
public final double getWeight() {
return this.weight;
}
@NotNull
public final String getColor() {
return this.color;
}
public final int getAge() {
return this.age;
}
public final void fly() {
}
}

可以看出,虽然 Kotlin 中类声明的语法非常近似 Java,但也存在很多不同:

1)不可变属性成员。正如我们在第 2 章介绍过,Kotlin 支持用 val 在类中声明引用不可变的属性成员,这是利用 Java 中的 final 修饰符来实现的,使用 var 声明的属性则反之引用可变。

2)属性默认值。因为 Java 的属性都有默认值,比如 int 类型的默认值为 0,引用类型的默认值为 null,所以在声明属性的时候我们不需要指定默认值。而在 Kotlin 中,除非显式地声明延迟初始化,不然就需要指定属性的默认值。

3)不同的可访问修饰符。Kotlin 类中的成员默认是全局可见,而 Java 的默认可见域是包作用域,因此在 Java 版本中,我们必须采用 public 修饰才能达相同的效果。我们会在下一节讲解 Kotlin 中不同的访问控制。

  1. 可带有属性和默认方法的接口

在看过类对比之后,我们继续来看看 Kotlin 和 Java 中接口的差异。这一次,我们先来看一个 Java 8 版本的接口:

复制代码
// Java 8 中的接口
public interface Flyer {
public String kind();
default public void fly() {
System.out.println("I can fly");
}
}

众所周知,Java 8 引入了一个新特性——接口方法支持默认实现。这使得我们在向接口中新增方法时候,之前继承过该接口的类则可以不需要实现这个新方法。接下来再来看看在 Kotlin 中如何声明一个接口:

复制代码
// Kotlin 中的接口
interface Flyer {
val speed: Int
fun kind()
fun fly() {
println("I can fly")
}
}

同样,我们也可以用 Kotlin 定义一个带有方法实现的接口。同时,它还支持抽象属性(如上面的 speed)。然而,你可能知道,Kotlin 是基于 Java 6 的,那么它是如何支持这种行为的呢?我们将上面 Kotlin 声明的接口转换为 Java 代码,提取其中关键的代码:

复制代码
public interface Flyer {
int getSpeed();
void kind();
void fly();
public static final class DefaultImpls {
public static void fly(Flyer $this) {
String var1 = "I can fly";
System.out.println(var1);
}
}
}

我们发现 Kotlin 编译器是通过定义了一个静态内部类 DefaultImpls 来提供 fly 方法的默认实现的。同时,虽然 Kotlin 接口支持属性声明,然而它在 Java 源码中是通过一个 get 方法来实现的。在接口中的属性并不能像 Java 接口那样,被直接赋值一个常量。如以下这样做是错误的:

复制代码
interface Flyer {
val height = 1000 //error Property initializers are not allowed in interfaces
}

Kotlin 提供了另外一种方式来实现这种效果:

复制代码
interface Flyer {
val height
get() = 1000
}

可能你会对这种语法感到不习惯,但这与 Kotlin 实现该机制的背景有关。我们说过,Kotlin 接口中的属性背后其实是用方法来实现的,所以说如果我们要为变量赋值常量,那么就需要编译器原生就支持方法默认实现。但 Kotlin 是基于 Java 6 的,当时并不支持这种特性,所以我们并不能像 Java 那样给一个接口的属性直接赋值一个常量。我们再来回味下在 Kotlin 接口中如何定义一个普通属性:

复制代码
interface Flyer {
val height: Long
}

它同方法一样,若没有指定默认行为,则在实现该接口的类中必须对该属性进行初始化。

总的来说,Kotlin 的类与接口的声明和 Java 很相似,但它的语法整体上要显得更加简洁。好了,现在我们定义好了 Bird 类,接下来再来看看如何用它创建一个对象吧。

Kotlin核心编程(45):面向对象 3.1.1

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

评论

发布