| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 开发测试 -> 高级语言基本世界观——类型系统 -> 正文阅读 |
|
[开发测试]高级语言基本世界观——类型系统 |
《高级语言基本世界观——类型系统》原文链接,阅读体验更佳! 可以说,类型系统是一门语言的世界观的基本规则,只有理解了一门语言的类型系统,才能真正掌握这门语言的核心。 不同语言的类型规则可能天差地别,但是我们往往会从两个维度来描述一门语言类型系统的基本特征,分别是:
同时,原子数据类型也是任何一门高级语言都会内建的基本特性,也是构成一门语言类型系统的基本特性。下面,我们就从这动/静和强/弱这两个维度来讨论一下高级语言类型系统的基本特性,同时简单说明一下原子数据类型在类型系统中的重要作用。
静态类型/动态类型在进行这部门内容的讨论之前,我们需要先明确以下几个事实:
而静态类型/动态类型所描述的其实是左值是不是具有数据类型约束。换句话说,其描述的是变量(左值)是不是只能存储某种类型的值。 静态类型如果左值只能存储某种类型的值,这个时候,这门语言就是静态类型的。 在静态类型的语言中,我们在声明一个变量的时候可能需要为这个变量指定数据类型,也有可能不需要指定,如果不需要指定那么编译器就会根据这个变量的初始化信息来推断出这个变量的类型,总之,在静态类型的语言中,一个变量只能存储某种类型的值,而不能随意使用。 静态类型的核心是变量只能存储某种类型的值,因此,一个变量在静态期必须具有一个明确且唯一的类型,而到了语言的运行时,变量中的值肯定是和我们声明变量时指定的数据类型是兼容的。 下面我们来理一下静态类型的特点:
动态类型而如果一门语言是动态类型的,那么我们在声明一个左值的时候,就不用指定它的类型,它可以存储任意类型的值,具体的类型要到语言的运行阶段才能确认。 这在很大程度上提高了编码的灵活性,但是同时也意味着所有的类型检查工作都需要放在运行时进行,在一定程度上会损耗性能。而且,由于我们并不知道左值中存储的值的实际类型是什么,我们的代码中可能需要很多处理类型的代码,在动态类型的语言中通常称这样的代码为能力检测代码。 静态类型 VS 动态类型上面我们已经介绍了静态类型语言和动态类型语言的本质区别:
我们来看一下下面的JavaScript代码和c++代码: 这两段代码所实现的功能是一样的,但是JavaScript是动态类型的,而C++是静态类型的,它们二者的对比如下: 通过上面的对比,二者的优势如下:
这么看下来,使用动态类型好像有点得不偿失,但是:
总之,在硬件越来越强大,软件迭代越来越快的今天,动态类型似乎也越来越受欢迎了。 灵活使用类型数据类型是高级语言抽象出来,为数据的存储和数据可以进行的操作提供元数据信息的。它代表了一门高级语言最基本的抽象能力。一门语言的数据类型的灵活性将直接影响整门语言的灵活性。 对于动态类型的语言,其类型本身就很灵活。动态类型的语言往往通过鸭子类型来最大化数据类型的抽象能力;对于静态类型的语言,类型的使用就相对没有那么灵活了,这种不灵活性是由于编译期对左值类型的限制造成的,所以很多静态类型的语言提供了类似泛型或者模板的功能,以提高类型使用的灵活性。 类型参数化静态类型可以提高程序的可读性和健壮性,可以在编译期就消除很多的bug;但是,我们上文也提到了,静态类型的语言由于在编译时限制了左值的类型,这在很大程度上限制了编码的灵活性,为代码的重用造成了一定的障碍,而代码的重用能力又是一门语言所亟待解决的问题——于是,静态类型成了一堵围城,城里的人想出去,城外的人想进来。 为了更大程度上提高代码的抽象能力和复用能力,很多静态类型的编程语言提供了泛型或者模板的功能,这种技术的核心思想就是类型参数化,把数据类型当做参数使用,让编译器根据类型参数生成对应类型的代码或者是进行静态类型校验。 比如C++中提供了模板的功能,编译器可以根据模板生成实际的代码;Java提供了泛型,编译器根据类型参数来进行静态类型校验,由于Java在实现泛型的时候采用了类型擦除的解决方案,所以Java中的泛型并不会生成实际类型的代码,而是在使用类型参数的地方自动插入类型转换的代码… 如下C++代码:
上述代码中的Goose和Duck这两个类之间并没有任何的继承关系,但是我们使用一个模板类把类型参数化之后,这两个类就可以使用同一个方法了。 C++的模板使用类预编译的处理方式,而预编译发生在代码的编译之前,所以C++的参数化类型更接近于我们下面要介绍的鸭子类型。而像Java中的泛型,它的能力检查是基于类型的,还要有所不同,下一篇文章中我们会进行介绍。
鸭子类型动态类型的语言本身就是非常灵活的,由于我们在编码的时候并不知道一个左值中的数据是什么类型的,而我们在动态类型的语言中使用一个左值的时候往往也不用太关注它是什么类型的,我们在使用的时候往往只关注一个值具有什么样的能力(或者说能进行什么样的操作)就可以了。 这种使用值的风格就是典型的鸭子类型——只要一只鸟,它看起来像鸭子、走起来像鸭子、游起来也像鸭子,但是我们并不知道这到底是一种什么鸟,那我们就认为它是一只鸭子。 比如下面的Python代码:
可以看到Goose和Duck这两个类之间并没有任何的继承关系,但是这两者有共同的能力,就是能够鸣叫,所以我们在调用like_duck_tweet方法的时候,这两个类的实例都是可以作为参数的。 从上面我们也可以看出,鸭子类型关注的是值的能力,而不是值的类型。而我们在使用类似风格的语言的时候,代码中也会出现非常多的能力检测代码,就像上面代码中的
灵活使用类型的本质提高类型的灵活性的核心思维其实和鸭子类型的思维是不谋而合的——我们只想关注值所拥有的能力,而不想关注值的类型。 其实我们在上文中介绍的c++模板的那段代码也在一定程度上体现出了鸭子类型的思维: 在 意思就是Human类中并没有符合使用要求的tweet()方法。这和鸭子类型的思维是差不多的,只不过能力检测是在静态阶段由编译器完成的。 当然有一些静态类型的语言的泛型并不是基于能力检测的鸭子类型,而是一种纯粹的类型判断,比如Java,但是静态类型中能力是基于类型的,所以说到底思想还是一样的: 最灵活的使用类型的模式是只关注值的能力而不关注值的类型。 GoLang——一只‘静态鸭’我们上面说明了各种语言中提高类型使用灵活性的方法,大致的套路如下:
但是事情并不是绝对的,上面我们已经介绍过了TypeScript中提供了静态类型检查的能力,其中的一个重要特性就是泛型;而我们这里要介绍的是GoLang。 Go语言是静态强类型的,其一直以精炼著称,其类型系统也是短小精悍,而网上讨论的比较多的就是Go语言到底是不是面向对象的编程语言——因为Go语言并不支持继承。 但是,在我的眼里,Go语言是支持面向对象编程范式的,只不过其更类似于基于原型的流派,而不是基于类的。同时其对多态的支持也是通过其接口的鸭子类型机制来实现的。 可以看到Go语言中灵活使用类型的做法却是动态类型常用的解决方案。 下面我给出一个在基于类型的面向对象语言中非常典型的应用场景——模板方法来说明一下:
上面的代码中定义了两个结构,ExecutorA和ExecutorB,其中ExecutorA是ExecutorB的原型。我们声明了一个接口,其中有两个方法stepOne()和stepTwo(),ExecutorA中有两个签名一致的方法, 同时,ExecutorB并没有实现stepOne方法,但是ExecutorA是它的原型,ExecutorA是实现了stepOne方法的,所以ExecutorB也就具有了stepOne的能力,最终静态能力检测得以通过。运行上面的测试方法,结果如下: 上面的Go代码其实还有很多需要注意的细节,这里我们不做过多的说明。 强类型/弱类型对于类型的强弱,这个概念一直是模糊不清的,并没有一个明确的定义。网上的解释也比较杂乱,而且往往是错误的解释。比如有人说因为在Python中下面的代码会抛出错误而认为Python是强类型的:
但是这这种解释是不太合理的,在Java中类似的代码是可以正常运行的,但是Java也是公认的强类型语言。 那么,到底该怎么定义强类型和弱类型的,二者是如何进行界定的呢?下面是我摘抄自维基百科的解释:
其实上面的解释也是不尽人意的,但是我们可以抓住关键的一点:用以描述编程语言对于混入不同数据类型的值进行运算时的处理方式。
也就是说,类型的强弱实际上描述的是,语言对类型的敏感程度。 其实在谈到类型的强弱的时候,我们更侧重的是类型的安全程度,即程序在编译时和解释时不是会检测一些非法的操作,以保证操作的安全和合法性。对于这一点最典型的例子就是数组访问的越界检查。 很多人都把类型的静/动和强/弱混为一谈,诚然,静态类型的编程语言对类型好像天生比较敏感,其看上去好像就是强类型的,其实这是因为静态类型本身固有的静态期类型检查的功能所造成的错觉,实际上强/弱跟静/动描述的是不同的维度。
比如C语言,它是静态类型的,静态类型检查是其固有的属性,但是其设计思想是——相信程序员,不妨碍程序员做任何事情。这就意味着其类型是比较弱的——它连数组访问是否越界都不会进行检查。 同时,对语言强弱类型的划分并没有明显的分界线,但是我们的直观感受是——如果一门语言对外声称自己是强类型的,那么我们在使用类型的时候就会受到更多的限制。 强类型 VS 弱类型强类型的语言对类型的使用会有更多的限制,由于要实现这些限制,程序在运行时往往需要作出一定的校验动作,这会消耗额外的性能;但是程序的安全性可以得到保证,不会或者说很少会发生莫名其妙的bug。 弱类型的语言对类型的限制较少,往往可以得到更好的性能,但也不是绝对的,比如JavaScript,由于在运行的时候要尽可能使得程序正常运行并产生结果,可能要进行各种各样的类型转换,反而会消耗性能。 就个人而言,本人更加喜欢强类型的语言,一个优秀的软件工程师在使用一门语言的时候应该是充分理解该语言的特性之后把自己限制在一个特定的子集里,以求自己的程序更加稳定,强类型的语言可以帮助我们完成这一点。而且,弱类型的语言往往会发生不可控的副作用,尤其是C语言这种本身非常底层的语言,发生的不可控副作用可能是具有毁灭性的。 标量数据类型关于标量数据类型、基本数据结构、高级数据结构的内容可以参考《数据结构中的原子、分子和物质》一文。 我们这里所说的标量数据类型其实跟字面值有一定的相关性,包括数字、字符串、布尔值等。 我们前面的文章中提到过,字面值是我们向程序中传递信息的最终方式,而标量数据类型具有这样的特点:
我们这里为什么要把标量数据类型单独拎出来呢? 这是因为,不管在哪一门语言中,标量数据类型都应该是语言的内建类型,用户通过自定义的方式声明的类型都不会是标量,而是一个具有结构的数据类型(某些语言中的枚举除外)。 通过上面的分析,我们可以得出这样一个结论:标量就是数据类型中的 如下js代码:
上面我们创建了一个对象obj(注意这不是创建了一个类型,而是一个对象,严格来说js中没有任何自定义类型的机制),它是具有结构的,而且里面还嵌套了一层,我们可以观察一下obj是不是最终是由一个个标量所组成的。
值集每一种数据类型都有其所能表示的值的范围,一个数据类型所能表示的所有的值我们就称之为这个数据类型的值集。 在讨论数据类型的值集的时候其实我们只需要关注标量数据类型的值集,原因有以下几点:
不同语言对于标量类型都具有不同的规则,相似标量类型的值集可能也有所不同,在我们介绍具体语言的时候会对语言的标量类型的值集有所介绍。 真假值布尔类型在任何一门高级程序设计语言中都是一个使用频率相当高的数据类型,因为任何的逻辑判断的结果都是由布尔类型的值表示的。为了编码方便,很多操作语言的类型系统都引入了
在支持真假值的类型系统中,一个值除了其本身所代表的值之外,还代表了一个逻辑值,支持真假值在动态类型系统中是比较常见的,比如在js中,下面的代码是非常常见的:
obj如果是null或者是undefined,那么我们最终得到的value是1。 也就是说,在支持真假值的类型系统中,逻辑表达式会返回一个真值或假值,而其数据类型还是其原来的数据类型,并不是一个布尔类型。这会给我们的编程带来极大的方便。 总结在这篇文章中,我们简要介绍了描述一门语言类型系统的两个方面:静/动和强/弱。这是两个维度,我们不应该把它们混为一谈。而文中,我们也留下了一个问题——静态类型的编程语言中灵活使用类型的常用手段是泛型,而泛型其实就是参数化类型,它允许我们在代码中定义类型变量,在真正使用的时候才决定具体的类型是什么。这其实为我们提供了一种用现有的类型构造更复杂类型的手段,同时也引出了另一个问题——变型。下一篇文章我们就来简单介绍一下静态类型的编程语言中的参数化类型和变型。 同时,这篇文章比较长,感谢你耐心读完。本人深知技术水平和表达能力有限,如果文中有什么地方不合理或者你有其他不同的思考和看法,欢迎随时和我进行讨论(laomst@163.com)。 |
|
开发测试 最新文章 |
pytest系列——allure之生成测试报告(Wind |
某大厂软件测试岗一面笔试题+二面问答题面试 |
iperf 学习笔记 |
关于Python中使用selenium八大定位方法 |
【软件测试】为什么提升不了?8年测试总结再 |
软件测试复习 |
PHP笔记-Smarty模板引擎的使用 |
C++Test使用入门 |
【Java】单元测试 |
Net core 3.x 获取客户端地址 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/18 0:23:54- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |