PL真有意思(五):数据类型

现在大多数程序设计语言中都有表达式和/或对象的类型概念。类型起着两种主要作用:

为许多操作提供了隐含的上下文信息,使程序员可以在许多情况下不必显示的描述这种上下文。比如int类型的两个对象相加就是整数相加、两个字符串类型的对象相加就是拼接字符串、在Java和C#中new object()隐含在背后的就是要分配内存返回对象的引用等等。

类型描述了其对象上一些合法的可以执行的操作集合。类型系统将不允许程序员去做一个字符和一个记录的加法。编译器可以使用这个合法的集合进行错误检查,好的类型系统能够在实践中捕获很多错误

类型系统

从编译方面的知识我们可以知道,计算机硬件可以按多种不同的方式去解释寄存器里的一组二进制位。处理器的不同功能单元可能把一组二进制位解释为指令、地址、字符、各种长度的整数或者浮点数等。当然,二进制位本身是无类型的,对存储器的哪些位置应该如何解释,大部分硬件也无任何保留信息。汇编语言由于仅仅是对一些二进制指令的“助记符号”翻译,它也是这种无类型情况。高级语言中则总是关联值与其类型,需要这种关联的一些原因和用途就如前面说到的上下文信息和错误检测。

一般来说,一个类型系统包含一种定义类型并将它们与特定的语言结构关联的机制;以及一些关于类型等价、类型相容、类型推理的规则。 必须具有类型的结构就是那些可以有值的,或者可以引用具有值得对象的结构。类型等价规则确定两个值得类型何时相同;类型相容规则确定特定类型的值是否可以用在特定的上下文环境里;类型推理规则基于一个表达式的各部分组成部分的类型以及其外围上下文来确定这个表达式的类型。

在一些多态性变量或参数的语言中,区分表达式(如一个名字)的类型与它所引用的那个对象的类型非常重要,因为同一个名字在不同时刻有可能引用不同类型的对象。

在一些语言中,子程序也是有类型的,如果子程序是一级或者二级值,其值是动态确定的子程序,这时语言就需要通过类型信息,根据特定的子程序接口(即参数的个数和类型)提供给这种结构的可接受的值集合,那么子程序就必须具有类型信息。在那些不能动态创建子程序引用的静态作用域语言(这种语言中子程序是三级值),编译器时就能确定一个名字所引用的子程序,因此不需要子程序具有类型就可以保证子程序的正确调用。

类型检查

类型检查时一个处理过程,其目的就是保证程序遵循了语言的类型相容规则,违背这种规则的情况称为类型冲突。说一个语言是强类型的,那么就表示这个语言的实现遵循一种禁止把任何操作应用到不支持这种操作的类型对象上的规则。说一个语言是静态类型化(statically type)的,那么它就是强类型的,且所有的类型检查都能在编译时进行(现实中很少有语言是真正的静态类型,通常这一术语是指大部分类型检查可以在编译器执行,其余一小部分在运行时检查)。如C#我们通常都认为它是静态类型化的语言。

动态(运行时)类型检查是迟约束的一种形式,把大部分的检查操作都推迟到运行的时候进行。采用动态作用域规则的语言大部分都是动态类型语言,因为它的名字和对象的引用都是在运行时确定的,而确定引用对象的类型则更是要在引用确定之后才能做出的。

类型检查是把双刃剑,严格的类型检查会使编译器更早的发现一些程序上的错误,但是也会损失一部分灵活性;动态类型检查灵活性大大的,但是运行时的代价、错误的推迟检查,各种语言的实现也都在这种利弊上进行权衡。

多态性

多态性使得同一段代码体可以对多个类型的对象工作。它意味着可能需要运行时的动态检查,但也未必一定需要。在Lisp、Smalltalk以及一些脚本语言中,完全的动态类型化允许程序员把任何操作应用于任何对象,只有到了运行时采取检查一个对象是否实现了具体的操作。由于对象的类型可以看作它们的一个隐式的(未明确声明的,一个不恰当的比喻就如C#中的this)参数,动态类型化也被说成是支持隐式的参数多态性。

虽然动态类型化具有强大的威力(灵活性),但却会带来很大的运行时开销,还会推迟错误报告。一些语言如ML采用了一种复杂的类型推理系统,设法通过静态类型化支持隐式的参数多态性。

在面向对象语言里,子类型多态性允许类型T的变量X引用了从T派生的任何类型的对象,由于派生类型必定支持基类型的所有操作,因此编译器完全可以保证类型T的对象能接受的任何操作,X引用的对象也都能接受。对于简单的继承模型,子类型多态的类型检查就能完全在编译时实现。采用了这种实现的大多数语言(如C++,JAVA和C#)都提供另一种显示的参数化类型(泛型),允许程序员定义带有类型参数的类。泛型对于容器(集合)类型特别有用,如T的列表(List)和T的栈(Stack)等,其中T只是一个类型占位符,在初始化的这个容器对象时提供具体的类型来代替它。与子类型多态类似,泛型也可以在编译时完成类型检查。比如C++的模板完全就是编译期间的东西,编译后就完全没有了模板的痕迹;JAVA则是利用一种“擦除”的技术实现的泛型,需要在运行时做一些检查。

类型的含义

现在至少存在三种不同的考虑类型问题的方式,分别称之为指称的、构造的和基于抽象的

指称的

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjysj.html