Appearance
类型系统概述
对于面向对象编程的支持,Go 语言的实现可以说是完全颠覆了以往我们对面向对象编程的认知。
Go 面向对象编程设计
Go 语言面向对象编程设计得简洁而优雅。
简洁之处在于,Go 语言并没有沿袭传统面向对象编程中的诸多概念,比如类的继承、接口的实现、构造函数和析构函数、隐藏的 this 指针等,也没有 public
、protected
、private
之类的访问修饰符。
优雅之处在于,Go 语言对面向对象编程的支持是语言类型系统中的天然组成部分,整个类型系统通过接口串联,浑然一体。
很少有编程类的书谈及类型系统这个话题,实际上类型系统才是一门编程语言的地基,它的地位至关重要。因此,这里我们将从类型系统入手介绍 Go 语言的面向对象编程特性。
类型系统概述
顾名思义,类型系统是指一个语言的类型体系结构。一个典型的类型系统通常包含如下基本内容:
- 基本类型,如
byte
、int
、bool
、float
、string
等; - 复合类型,如数组、切片、字典、指针、结构体等;
- 可以指向任意对象的类型(Any 类型);
- 值语义和引用语义;
- 面向对象,即所有具备面向对象特征(比如成员方法)的类型;
- 接口。
类型系统描述的是这些内容在一个语言中如何被关联。因为 Java 语言自诞生以来被称为最纯正的面向对象语言,所以我们就先以 Java 语言为例讲一讲类型系统。
Java vs Go 类型系统设计
Java
在 Java 语言中,存在两套完全独立的类型系统:
- 一套是值类型系统,主要是基本类型,如
byte
、int
、boolean
、char
、double
等,这些类型基于值语义; - 一套是以
Object
类型为根的对象类型系统(类),可以定义成员变量、成员方法、虚函数(抽象函数)等,这些类型基于引用语义,只允许在堆上创建(通过关键字new
)。
Java 语言中的 Any 类型就是整个对象类型系统的根 —— java.lang.Object
类型,只有对象类型系统中的实例才可以被 Any 类型引用。值类型想要被 Any 类型引用,需要经过装箱 (boxing)过程,比如 int
类型需要装箱成为 Integer
类型。
另外,在 Java 中,只有对象类型系统中的类型才可以实现接口。
Go
相比之下,Go 语言中的大多数类型都是值语义,包括:
- 基本类型,如布尔类型、整型、浮点型、字符串等;
- 复合类型,如数组、结构体等(切片、字典、指针和通道都是引用语义);
这里的值语义和引用语义等价于之前介绍类型时提到的值类型和引用类型。
为值类型定义成员方法
所有值语义类型都支持定义成员方法,包括内置基本类型。例如,我们可以为整型新增比较方法 Equal
,不过在此之前,需要将基本类型通过 type
关键字设置为新的类型:
Go
type Integer int
func (a Integer) Equal(b Integer) bool {
return a == b
}
这里,我们基于 int
设置了 Integer
类型(这是一个新类型,不是类型别名),在使用的时候也要通过 Integer
类型进行声明,不过本质上它仍然是一个整型数字,你可以把具体的数值看做是 Integer
类型的实例。
接下来,就可以在整型类型上调用 Equal
方法了:
Go
func main() {
var x Integer
var y Integer
x, y = 10, 15
fmt.Println(x.Equal(y))
}
这有点像 Java 的装箱功能。当然了,你还可以为 Integer
类型添加更多方法:
Go
// 加法
func (a Integer) Add(b Integer) Integer {
return a + b
}
// 乘法
func (a Integer) Multiply(b Integer) Integer {
return a * b
}
这样一来,就将基本的数字类型就转化成了面向对象类型。
接口实现
而在实现某个接口时,只需要实现该接口要求的所有方法即可,无需显式声明实现的接口(实际上,Go 语言根本就不支持传统面向对象编程中的继承和实现语法)。例如,如果有如下这个接口存在:
Go
type Math interface {
Add(i Integer) Integer
Multiply(i Integer) Integer
}
type Integer int
// 加法
func (a Integer) Add(b Integer) Integer {
return a + b
}
// 乘法
func (a Integer) Multiply(b Integer) Integer {
return a * b
}
Interger
类型实现了 Math
接口中 Add
、Multiply
两个方法,则认为 Integer
实现了 Math
接口,无需显式声明。
任意类型
任何类型都可以被 Any 类型引用。在 Go 语言中,Any 类型就是空接口,即 interface{}
。
在接下来的几篇教程中,学院君会对 Go 语言类型系统的特点和面向对象编程进行详细介绍。