冒号课堂
第五课 语言小谈(2)
5.2数据类型——规则与变通
操纵于规矩之中,神明于规矩之外 ——《俞震·古今医案按》
关键词: 数据类型,静态类型,动态类型,Duck类型,强类型,弱类型,类型安全
摘要: 关于数据类型的讨论
!预览
· Duck类型的哲学是:名义不重要,重要的是能力
· 将一个会叫会游的家伙放进池塘看起来不算坏主意,但如果一艘轮船趁机也轰隆隆地开了进来,事情恐怕就不那么美妙了
· 静态类型检查类似“疑罪从有”的有罪推定制,动态类型检查类似“疑罪从无”的无罪推定制
· 尽可能守规则,必要时求变通
· 规则如裤带,过于宽松和过于束缚都不好
?提问
· 动态语言与动态类型语言是一回事吗?
· 数据类型有哪两个要素?其意义何在?
· 什么是动态类型和静态类型?它们的区别是什么?各有什么优缺点?
· 什么是鸭子类型(duck typing)?它有什么优缺点?
· 什么是强类型与弱类型?什么是类型安全的?
:讲解
待教室平静下来,冒号再度开腔:“在谈论动态语言之前,最好先澄清一下它与动态类型语言之间的区别。”
叹号讶然道:“它们不是一回事吗?一直以为动态语言是动态类型语言的简称呢。”
“有亲戚之名,却无血缘之亲。名称上相似,加之动态语言绝大多数确是动态类型语言,造成混淆实属在所难免,但二者之间并无必然联系——动态语言不一定是动态类型语言[1],动态类型语言也不一定是动态语言[2]。”冒号飞跑的舌头几乎绊蒜,同时把众人的脑子搅成了一锅粥。
见势不妙,冒号改用迂回战术:“我们不妨再谈开些,大家对数据类型是如何理解的?”
逗号随口道:“数据类型不就是数据的种类吗?”
众人暗笑:说了跟没说差不多。
冒号说道:“数据类型包含两个要素:一个是允许取值的集合,一个是允许参与的运算。例如int类型在Java中既定义了介于− 231 和231 − 1之间的整数集合,也定义了该集合上的整数所能进行的运算。现在的问题是:数据类型的意义何在?”
句号回答:“限定一个变量的数据类型,就意味着限制了该变量的取值范围和所参与的运算,这从一定程度上保证了代码的安全性。”
冒号追问:“还有吗?”
句号略作思考后说:“用户自定义的数据类型,如C中的结构和Java中的类或接口,赋予数据以逻辑内涵,提高了代码的抽象性。”
“精辟!”冒号赞道,“数据类型既有针对机器的物理意义,又有针对人的逻辑意义。前者用于进行底层的内存分配和数值运算等,后者用于表达高层的逻辑概念。既然类型如此重要,类型检查就必不可少了[3]。所谓动态类型语言(dynamic typing language),正是指类型检查发生在运行期间(run-time)的语言。”
“那静态类型语言(static typing language)自然是类型检查发生在编译期间(compile-time)的语言咯。”引号接话道。
冒号回应:“一般的说法是这样,但我更愿意将‘编译期间’四个字改为‘运行之前’,否则容易让人误解为静态类型语言一定是编译型语言(compiled language)。”
问号问道:“是否可以这么说:静态类型语言需要变量声明,而动态类型语言则不需要?”
“这话只对了一半。”冒号评论,“动态类型语言固然不需要显式的变量声明(explicit declaration),一些静态类型语言有时也不需要。典型的如ML、Haskell之类的函数式语言,编译器可以通过上下文来进行类型推断(type inference)。”
“如何进行类型推断?”问号有点丈二和尚摸不着头脑。
冒号打了个比方:“假设‘+’号只限于同类型的数据运算,那么从表达式a + 1中可以推出a是整型变量,从b + 1.0中推出b是浮点型变量,从c + “1”中推出c是字符串型变量。这些变量不必事先声明,但一旦类型被推断确定后,便不再更改。由于这些推断都是在程序运行之前进行的,因此仍属于静态类型。它既有动态类型的简洁性,又不失声明式静态类型的安全性,可谓裁长补短啊。”
叹号有些羡慕地说:“还是动态类型语言好,不仅不必声明变量,而且一个变量在不同地方还可以代表不同类型,多省事多方便啊!”
冒号微微颔首:“虽然这种机制也有为人诟病之处,但不可否认,动态类型语言的确有它的优势:简明、快捷、灵活,并且天然具有泛型(generic)特征。值得一提的是,动态类型有一种被称作鸭子类型(duck typing)的形式。”
逗号感到有趣:“鸭子类型?很滑稽的名字。”
“这种类型通俗的说法是:如果一个对象既会走鸭步又会呷呷叫,何妨将其视作鸭子呢?”冒号说着投影出一段Ruby代码——
class Duck #会叫会游的鸭
def shout
puts '呷呷呷'
end
def swim
puts '鸭泳'
end
end
class Frog #会叫会游的蛙
def shout
puts '呱呱呱'
end
def swim
puts '蛙泳'
&