每學一門新的編程語言時,在看到介紹該門編程語言的特點時,經常會遇到 靜態、動態、強、弱 、隱式、顯式 類型等字樣,似懂非懂,這里結合網上的資料總結一下它們的含義以及區別,描述不一定專業、准確,但求能進一步理解這些詞的概念即可。
類型系統(Type System)用於定義如何將編程語言中的數值和表達式歸類為許多不同的類型,如何操作這些類型,這些類型如何互相作用。根據這些種種不同,可以將編程語言分為以下類別:
靜態類型編程語言 vs 動態類型編程語言
在靜態類型語言,每個變量名字都綁定到:
- 一個類型,編譯時通過變量的定義來綁定
- 一個對象,這是可選的,如果變量名沒綁定到一個對象,那么名字指向 null
例如下面是 Java 中定義字符串類型變量:
String str1; //reference to null
String str2 = "Hello world";
定義了兩個變量 str1、str2,它們的類型均是 String
類型,而 str1 並沒有指向特定的對象,但 str2 指向了一個 "Hello world" 的字符串對象。
注意的是,靜態類型的編程語言,一旦變量名綁定到一個類型(通過聲明語句),那么它就只能綁定到這種類型的對象上(通過賦值語句)。如果試圖該某種類型的變量綁定到不同類型的對象上,將在編譯時拋出類型異常的錯誤。
例如下面的代碼將一個整型的對象賦值給 String
類型的變量會報錯:
String str1;
str = 10;
而在動態類型語言中,每個變量名只於對象進行綁定。在程序運行時通過賦值操作來將名字綁定到對象上,如果再將名字綁定到另一種不同類型的對象上也是允許的。
例如下面是 Python 中代碼:
var = "Hello world"
//...
var = 10
先將 var 變量賦值一個 "Hello world" 字符串對象,運行過程中隨時可以將 var 賦值其它類型的對象。這在 Python 中並不會報錯。
下圖可以反應兩者的區別:
由上可以看出 Java、C/C++、C# 等屬於靜態類型編程語言,而 Python、PHP、Perl 等屬於動態類型語言。
強類型編程語言 vs 弱類型編程語言
根據變量能否隱式(implicit)轉換為無關(unrelated)的類型,可以將編程語言分成弱類型編程語言和強類型編程語言,前者可以完成轉換,后者則需要通過顯式(explicit)地轉換。注意這里的無關類型轉換指的是類似於數值類型與字符串類型的轉換,而大多數語言允許相關類型之間的隱式轉換,例如 int 類型到 float 類型的轉換。
在弱類型編程語言中,數字 9 和字符串 "9" 是可以相互轉換的,例如下面的代碼是合法的:
a = 9
b = "9"
c = concatenate(a, b) // produces "99"
d = add(a, b) // produces 18
但是在強類型編程語言中,后面兩條語句會拋出類型異常,為了避免這些異常,我們往往需要通過一些顯式類型轉換操作來完成:
a = 9
b = "9"
c = concatenate(str(a), b)
d = add(a, int(b) )
根據上面的描述,我們可以知道像 Python、Java 等屬於強類型編程語言,而 Perl、PHP 等屬於弱類型編程語言。
顯式類型編程語言 vs 隱式類型編程語言
還有一種區分方法是,根據變量名是否需要顯式給出類型的聲明,來將語言分為顯式類型語言和隱式類型語言。前者需要在定義變量時顯式給出變量的類型,而后者可以使用類型推論來決定變量的類型。
大多數靜態類型語言,例如 Java、C/C++ 都是顯式類型語言,但是有些則不是,如 Haskell、ML 等,可以基於變量的操作來推斷其類型;Scala 是一種新型的靜態類型編程語言,它運行在 Java 虛擬機上,也是使用的是類型推斷;Boo 是一種類 Python 編程語言,運行在 .NET CLI 之上,使用的是類型推斷。
總結
這篇文章只是給出了一個非形式化定義,旨在理解各種不同編程在類型系統的設計思想,當然本文只提到類型系統的冰山一角。