编程语言的数据类型模型

在对任何编程语言的讨论中,争议较大的一个问题就是类型模型。类型决定可以使用哪些种类的工具,并影响到应用程序的设计。

如果您想理解一种新的编程语言,甚至一系列语言,那么通常应该从类型策略着手。因为计算机语言之间的本质差别是类型系统,语言发展的总趋势是越来越抽象,越来越接近现实世界,而不是机器的世界。

语言的类型策略

至少可以从三个角度来看待语言的类型:

静态类型还是动态类型,这取决于何时实施类型模型。静态类型语言在编译时实施类型。而动态类型语言通常基于一个对象的特征在运行时实施类型。静态动态,表明 类型系统 的运作的时机,或者说和执行方式相关,编译语言是静态类型解释语言是动态类型

强类型还是弱类型,这取决于如何实施类型模型。所谓强弱,表明类型系统的类型实施的严格程度。强类型严格地实施类型,如果发现有违反类型规则的情况,则会抛出运行时或编译时错误。而弱类型则留有更多的余地。极端情况下,弱类型语言 (例如 Assembler)允许将任意数据类型赋给另一种类型(不管这种赋值是否有意义)。静态类型的语言既可以有强类型,也可以有弱类型(C是弱类型,Java是强类型);而动态类型系统通常 是强类型的,但也不完全是(PHP是动态解释性语言,却是弱类型的语言)。
有些人将强类型语言弱类型语言称之为类型语言无类型语言

显式类型还是隐式类型,这取决于语言如何确定一个给定对象的类型。显式类型语言要求声明每个变量和每个函数参数。而隐式类型语言则根据语言中的语法和结构线索来确定对象的类型。静态类型语言通常是显式类型的,但也不完全是(OCaml是静态类型语言 Lisp 的衍生物,采用自动类型判断,是隐式类型的);而动态类型语言几乎都是隐式类型的。



常用编程语言的类型模型
语言 何时实施 如何实施 如何确定 备注
C 静态 显式 半弱类型**
C++ 静态 显式 面向对象,稍弱
Java 静态 显式 面向对象***
Python 动态 隐式  
Ruby 动态    
PHP 动态 隐式  
Perl 动态      
TCL        
Fortran        
Basic  动态      
Pascal/Delphi  静态      Delphi 面向对象
Lisp        
Ada        
C#  静态      
Javascript  动态      
Asm  静态      汇编无所谓类型*
D
 静态      

**C是半弱类型,因为相对于 汇编语言而言,
1)每个变量都必须被声明为整型或指针或字符串等特殊类型,并且必须用于适合这种类型变量的方法。比如字符串你不能实施 适用于 整型变量的加减乘除,必须有一个显式或隐式的类型转换;
2)变量可以集中在结构体中或者定义好的子结构体和过程或方法的对象中以便于使用;一种类型的对象不能用于 期待其他类型对象处. 比如 不能把一个整型变量 赋给一个结构体 变量。
上面这些,表现了C作为强类型的语言的特征。
但是,它又表现出 弱类型的一面(相对于Java Python等强类型语言而言)
1)可把字符型变量 赋给 一个整型变量,虽然有警告,但是可编译通过
2)你可在一个字符型变量,整型变量,长整型变量,浮点变量间实施 运算,会有一个自动的类型提升
3)void * 可以代表任何类型的指针
*** 完全的面向对象要求对象是强类型的。强类型是只在对象类型的定义尽量的细化。我们可以定义所有的对象都是object,然后允许后期绑定来对对象进行操作。Java/Python/Ruby等都是强类型的面向对象的语言。

动态类型好还是静态类型好?

静态类型的优点


在静态类型语言中,程序员(通过声明或根据约定)或编译器(根据结构和语法线索)将一种类型指定给一个变量,然后那个类型就不会改变。静态类型通常需要额 外的成本,因为静态类型语言(例如 Java 语言)通常是显式类型的。这意味着必须声明所有的变量,然后编译代码。成本也伴随着收益:早期的错误检测。静态类型在最基层为编译器提供多得多的信息。更 多信息所带来的好处就是,可以更早地捕捉到某些类型的错误,而动态类型语言只有到运行时才能检测到这些错误。如果您一直等到运行时才捕捉这些 bug,那么其中一些将进入生产环境。也许这正是动态类型语言受到最多指责的一个方面。

另一种观点则认为现代软件开发团队通常会运行自动测试,动态语言的支持者声称,即使是最简单的自动测试也可以捕捉到大多数的类型错误。而动态语言的支持者 所能提供的对编译时错误检测不利的最好论据是,早期检测所带来的好处相对于成本来说是得不偿失的,因为不管是否使用动态类型,最终都要进行测试。

一种有趣的折中方法是在静态类型语言中使用隐式类型,从而减少类型的成本。
开放源代码语言 Objective Caml (OCaml) 是静态类型语言 Lisp 的衍生物,它既能提供很好的性能,又不会牺牲生产率。也就是采用所谓自动类型判断。

静态类型可以提供更好的安全性,而且显然还可以提高代码的可读性。
静态类型还可以提供更多的信息,使得编译器更容易进行优化,从而提高性能。
但是静态类型赢得开发人员青睐的最大原因是更容易检测错误,而且有更多可用的工具。

动态类型的优点


大多数偏爱动态类型的开发人员除了强调早期错误检测会带来不必要的成本外,还提到动态类型语言具有很好的可表达性和生产率。很简单,您通常可以用更少的关 键词表达更多的思想。作为一名新的 Ruby 拥护者,我深信动态语言更能提高生产率,虽然我不能比常见的静态语言的支持者拿出更多具体的证据来。但是,从我开始编写更多的 Ruby 代码起,我就感觉到自己的生产率有了明显的提高。诚然,我仍然会看到静态类型的优点,尤其是在工具集方面,但是我逐渐认识到了静态类型的缺点。

动态类型在原始代码行方面也可以节省精力。由于动态语言几乎都是类型推断式的,所以您不需要花多大力气来表达基本思想。变量无需声明即可直接使用。您也不 必表达参数类型的所有可能排列,只需输入一组名称。您的代码可以更加具有多态性 —— 任何对一种类型的方法有反应的对象都可以看作这种类型 —— 所以通常可以比其他语言更精简地表达思想。代码中的耦合也可以变得更松散。当您想改变某个东西的类型时,这种变化所波及的范围很有限,所以不需要在更多的 地方作出相应的更改。

被解释的语言由于没有编译时间而提供快速的转换.通过允许用户运行时编写应用程序,解释器使应用程序更加灵活。
动态语言不如静态语言效率高,部分是因为他们使用解释器而不是编译器,而且因为他们基本组件的选择标准是功能强大和易于使用而不是有效地对应基本 硬件.例如,脚本语言经常使用长度可变的字符串,而同样的情况下系统程序设计语言使用对应一个机器字的二进制值;脚本语言经常使用哈希表,而系统程序设计 语言使用变址阵列.

安全性还是灵活性


从某种意义上说,语言的静态与动态之争的关键在于安全性与灵活性之间的取舍。静态语言的支持者相信更安全的语言更好。而动态语言的支持者却不愿意为安全性 付出任何代价。对于他们而言,对一种语言的衡量标准在于能多快地表达思想,目标在于最大化程序员的效率。而另一方面,静态语言专家则说,如果能 在早期捕捉到 bug,那么就应该 这么做,而且工具可以弥补语言中的限制。

生产率提高的最后一个原因是减少了编译环节。很多动态类型语言是解释性的,所以在编写程序后可以立即看到变化。即使没有惯用的调试器,在 Ruby 中探索库和应用程序代码的行为也更为容易,因为您可以打开一个解释器,通常可以直接在调试会话中打开


隐式类型好还是显式类型好?


隐式类型语言可以拥有 Java 语言所具有的所有类型安全性,甚至更多。不同之处在于您需要提供的信息量,以及在阅读程序时可用的信息量。很多喜欢静态类型的人更偏爱隐式类型。他们宁愿让编译器来做这种事情,而不愿意被迫重复地在代码中输入变量的类型。

显式类型的好处是
第一,声明变量如何使用使大型程序更易于管理并区分那些必须被分别对待的变量.
第二,编译器可以利用类型信息来侦测某些类型的错误,比如,试图把一个浮点 值作为一个指针.
第三,定义类型能使编译器更好的执行特殊代码.例如,如果编译器知道一个变量总是对整型值有效,那么他就可以产生一个整型指令来操纵这个 变量;如果编译器不知道变量的类型,那么他就必须产生额外的指令在运行时检查变量类型.

强类型好还是弱类型好?

强类型语言的优点

强类型语言,变量的类型不可随意转换,如果不同类型的变量相互间赋值,就会在编译时(静态)或者 运行时(动态) 报错;弱类型的语言,变量的类型可以随需要任意转换,而且通常都是隐式的。

弱类型的缺点是,有时候 会有 程序员所不希望的类型的自动转换。如果用弱类型语言来编程,表面上简化了程序员在编程时对类型的考虑,实际上带来了更多的程序不可靠性,加重了测试工作难度(例如强类型可能有几个测试分 支,弱类型可能多达几十上百) 。弱类型实际上降低了程序员对要解决问题的了解程度,程序员对要解决问题的了解越浅,构筑的模型就越模糊,上手编程出的问题就更 多。比如 C语言中void *类型指针

汇编语言 寄存器类型和存储单元类型,只跟数据宽度有关,无所谓类型(变量类型几乎没有区分)

弱类型语言有两种:
一种是所谓 半弱类型,由程序员来操作 类型转换,转换错了自己负责,比如汇编和C
另一种是  由解释器进行运行期转换,减少程序员工作,增加了执行期开销。比如PHP

C++是强类型语言,但本身却存在隐式转换,这也给它的强类型性上打了一些折扣,不过,对于自定义类型来说,C++还是表现出了强类型语言的特点。

弱类型语言的好处

脚本语言一般都是弱类型的, 所有的东西无论是看起来还是使用起来都是完全一样的,因此他们可以互换.例如,一个变量可以一会儿处理字符串,一会儿又处理整型.代码和数据也常可互换,因此,可以用一个程序写另一个程序,然后高速执行,脚本语言一般是面向字符的,因为它为许多不同的事物提供了一致的描述.

弱类型语言使组件更容易连在一起.在使用时没有优先级限制,并且所有的组件及其值都用统一的方式描述.除此之外,任何组件和值都可以在任何情况下使用;为某一目的而设计的组件可以被用于设计者完全没有预见过的完全不同的目的.例如,在Unix shells中,所有的过滤程序从输入读入字节流,并把字节组成的字符串写入输出;任何两个程序都可以通过把一个的输出连到另一个的输入而把两者联系起来.

看起来脚本语言的弱类型特性(并不是所有的动态脚本语言都是弱类型,Python就是强类型。)会让人不能发现错误,但实际上脚本语言和系统程序设计语言一样安全。


经常有些人概念不清楚,逻辑混乱,还喜欢与人争论。总是把 弱类型与 动态类型等同起来,认为所有动态脚本语言都是弱类型,所有静态类型语言都是强类型。
还说出一下什么"汇编语言的类型是最强的","动态类型识别机制进一步弱化了类型系统"等贻笑大方的话。强弱跟动态静态没有什么必然的联系,类型强弱和语言本身的类型系统设计相关。

根据上面的定义和分析,我们通过几个例子来 证明各种语言到底是不是属于那种类型

假设您编译下面这段 Java 代码:
class Test {
public static void test(int i) {
String s = i;
}
}

会收到如下错误消息:

Test.java:3: incompatible types
found : int
required: java.lang.String
String s = i;
^
1 error

执行以下 Ruby 代码:

1 + "hello"

会收到以下错误消息:

TypeError: String can't be coerced into Fixnum
from (irb):3:in '+'
from (irb):3


这两种语言都倾向于强类型,因为当您试图使用一个它们期望之外的类型结构的对象时,它们都会抛出错误消息。Java 类型策略在编译时给出错误消息,因为它执行静态类型检查。而 Ruby 则是在运行时给出错误消息,因为 Ruby 支持动态类型。换句话说,Java 在编译时将对象绑定到类型。而 Ruby 则是在运行时每当更改对象的时候将对象绑定到类型。由于我是在 Java 代码中,而不是在 Ruby 中声明变量的,因此可以看到 Java 语言的显式类型与 Ruby 的隐式类型的工作方式不同。


 


作者:Gavin   更新日期:2006-10-15
来源:upsdn.net   浏览次数:

相关文章

相关评论   发表评论