类型
💡 tony白专栏的笔记
比如我们在学习一门新的语言的时候,可以考虑以下几个问题:
这门语言是强类型的吗?
这门语言是动态类型吗?
它支持多少种内建类型呢?
它支持结构体吗?
它支持字典 (Recorder) 吗?
它支持泛型吗?……
类型
编程语言中的变量都是有类型的,而且变量的类型不一定一致。例如 Go 语言中的 int 和 float 声明的变量,它们的类型就不一致,如果你直接对它们执行加操作,Go 的编译器就会报错,很多隐式类型转换带来的问题,在编译阶段就可以发现了。比如,你可以看下面这个例子:
|
|
这种情况下,Go 的编译器会报这样的错误:invalid operation: a + b (mismatched types int and float64)。这就说明 Go 语言不支持整型和浮点型变量的加操作。
Go 语言的类型系统还有一个特点,那就是一个变量声明成什么类型的,就不能再更改了。与之形成鲜明对比的是 Python。它们都具有比较高的类型强度,但是类型检查的时机不同。Go 是在编译期,而 Python 则是在运行期
。我们看一个 Python 的例子:
|
|
从上面的例子中,我们可以看到,Python 的类型检查也是比较严格的,在第 2 行,将一个整型值和字符串值相加是会引发 Traceback 的。但是一个变量却可以先使用整数为它赋值,再使用字符串为它赋值(第 6 行),然后再进行字符串值的加操作就没有问题了。这就说明了变量的类型是在程序运行的时候才去检查,而不是编译期间进行的。我们把这种语言称为动态类型语言
。
同样的例子如果使用 Go 来实现,编译器就会报错:
|
|
由此可见,Go 语言是一种静态的强类型语言。
动态类型不仅仅表现在变量的类型可以更改,在面向对象的编程语言中,动态类型往往还意味着类的定义也可以动态更改。我们仍然以 Python 为例来观察动态类型的特点:
|
|
从上述例子中,我们可以看到,类 A 的类属性是可以在运行时进行添加和修改的。这与静态编译的语言非常不同。
由此,我们可以得出结论,动态类型相比静态类型,它的优点在于:
- 动态类型有更好的灵活性,在运行时可以修改变量的类型,也可以对类定义进行修改,所以针对动态类型语言的热更新就更容易设计;
- 动态类型语言写起来很方便,写起来很
丝滑
。非常适合用来编写小规模的脚本
。
同时,动态类型也往往具有一些缺点(通常是这样,但并不绝对)。
- 首先,动态类型语言的代码不容易阅读。据统计,程序员的日常工作中 90% 的时间是在阅读别人写的代码,只有不足 10% 的时间才是在开发新的功能。而动态类型语言,没有类型标注,代码会非常难懂(
我翻了之前写的py代码,准确的说是脚本,是很少有带参数的,即使带了,也看不出来参数的类型是什么
),即使有一些动态类型语言有类型标注的,但因为可以运行时修改类型,往往会出现一个类的属性在不同的地方被修改的情况,这使得代码的阅读和维护变得困难; - 第二点是,动态类型语言的性能往往会差一些,比如 Python 和 JavaScript,因为在编译期间缺少类型提示,编译器无法为对象安排合理的内存布局(你可以参考内存课导学三),所以它们的对象布局相比 Java/C++ 等静态类型语言会更加复杂,同时这也会带来性能的下降。
由此可见,我们并不能简单地说,静态类型就比动态类型好,或者强类型就比弱类型好,还是要根据具体的场景来进行取舍。
比如要求快速开发,规模较小的工具,人们常常会选择使用 Python,而多人合作的大型项目,人们就会选择使用 Java 之类的静态强类型语言。
另外,类似 int、 String 这种类型往往是语言的内建类型,而语言的内建类型在表达力上经常是不够的,这就需要人们通过将简单内建类型组合起来实现相应的功能,这就是复合类型。
典型的复合类型包括枚举、结构、列表、字典等。这些类型在 Go 语言中都有相应的定义,你可以参考 Go 语言专栏进行学习。
泛型
泛型简单来说,就是使用类型得到新的类型。PY是没有泛型的,也不需要有。泛型这一块我还不是很理解,B站有个Go泛型实战课程,可以搂一眼。要多花时间去看。后面的博客会有介绍,就不多说了。
总结
从这些例子中,我们看到静态强类型语言更容易阅读和维护,但灵活性不如动态弱类型语言。所以动态弱类型语言往往都是脚本语言,不太适合构建大型程序。