谈谈Go语言宏观理解之三 - 类型系统 | 青训营笔记

146 阅读4分钟

在 Go 语言官方文档中读到了 Go 语言设计理念的内容,讲的十分全面。计划用系列文章整理一下对Go语言的宏观理解。主要是翻译自原名为 Go History 的文章:Frequently Asked Questions (FAQ) - The Go Programming Language: go.dev/doc/faq

先列一下文章链接:

这篇文章将概述Go语言类型系统的设计理念。

面向对象?

Go 语言可以说是面向对象编程语言,也可以说不是。Go 语言虽然有类型和方法,并且能够进行面向对象编程,但是 Go 语言没有类型间的显式依赖关系。Go 语言提供了与传统印象不一样的接口概念,不过开发者都认为这种接口更加易用并且在某些方面更加通用。也有与传统编程语言中子类类似的机制,可以在类型中嵌入其他类型。更重要的是,Go 语言中的方法比 C++ 或者 Java 中的方法更加普遍:可以给任何数据类型声明方法,即使是像整数之类的基本数据类型也可以声明方法。方法并不局限于结构体(或者说类型)。

此外,由于不存在类层次结构,Go 语言对象相比 C++ 或 Java 对象更加轻量化。

为什么没有类型继承?

面向对象编程,至少在最著名的传统编程语言中,都涉及到复杂的类型间关系。Go 语言的类型系统则与之不同。

相比传统编程语言要求程序员提前声明两个相互关联的类型,在 Go 语言中一个类型自动满足(satisfy)声明了相同方法的接口。除了减少书写工作量之外,这种隐式类层次结构是有实际好处的。类型可以同时满足多个接口,免去了传统编程语言中多继承的复杂性。接口因此也变得十分轻量化——可以用仅带有一个甚至 0 个方法的接口来表达一个概念。产生一个新想法或者需要测试时,可以事后按需添加新的接口,而不需要改动或者注释原有的代码。由于类型和接口之间不存在明确的关系,也就不需要讨论或维护类层次结构。

利用这种思想,可以构造出类型安全的 UNIX 管道。例如:fmt.Fprintf 可以打印到任意输出,不仅仅是文件;bufio 包可以和文件 IO 完全独立;image 包可以生成压缩的图片文件。所有这些成果都源于代表一个 Write 方法的 io.Writer 接口。上述还只是表面现象。Go 语言的接口对程序结构有深远的影响。

当然,这种隐式类型结构需要一定时间的适应,但它正是 Go 语言中最具生产力的部分。

静态链接?

Go 语言中动态分派方法的唯一来源就是接口。结构体或其他任何实体类型的方法一定是通过静态分派来解析。

为什么不支持重载方法或运算符?

如果方法分派时根本不需要进行类型匹配的话,语言实现层面就得到了极大的简化。从其他语言的经验中,我们了解到很多名字相同但签名不同的方法虽然偶尔有些作用,但在实践中常常令人困惑因而不利于项目长期稳定维护。仅通过方法名进行匹配,并要求参数类型一致是Go语言在类型系统中作出的重大简化。

至于运算符过载,该特性并不是一门编程语言不可或缺的功能,更多程度上是用来方便开发的语法工具。出于同样的理由,Go语言省略了这一特性:虽然运算符过载是一种便捷,但不提供运算符过载使得语言实现和性能优化更加简单。