1. 程式人生 > >程式語言的型別系統

程式語言的型別系統

每學一門新的程式語言時,在看到介紹該門程式語言的特點時,經常會遇到 靜態動態 、隱式顯式 型別等字樣,似懂非懂,這裡結合網上的資料總結一下它們的含義以及區別,描述不一定專業、準確,但求能進一步理解這些詞的概念即可。

型別系統(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 中並不會報錯。

下圖可以反應兩者的區別:

static-dynamic-typed-language

由上可以看出 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 之上,使用的是型別推斷。

總結

這篇文章只是給出了一個非形式化定義,旨在理解各種不同程式設計在型別系統的設計思想,當然本文只提到型別系統的冰山一角。

本文絕大數內容編譯自這篇文章,還參考 知乎問答,wiki 相關詞條,特致謝!