Day 3:資料型態的夢魘——動態型別加弱型別(2)
上一篇介紹了動態型別和靜態型別的差別,這一篇來看到弱型別和強型別。
強型別的例子,我們一樣拿型別界的乖寶寶 —— Java 為例,企圖在數字運算過程混進一個字串:
int x = 123 + "456";
System.out.println(x);
不出所料,得到一個編譯錯誤:
HelloWorld.java:4: error: incompatible types: String cannot be converted to int
很明確的告訴你,在 Java 不能用隱喻的方式,企圖直接將字串轉成整數,如果你想實現的是將「數字
123
」和「字串 "456"
轉成的數字」相加,你必須用 Java 能接受的方式明確表明,比如:int x = 123 + Integer.parseInt("456");
System.out.println(x);
執行結果:
579
這就是強型別的語言,偏向不容忍隱性的型別轉換。
同樣的寫法,換成弱型別會發生什麼事?
以 PHP 為例:
$x = 123 + "456";
echo $x;
執行結果:
579
不僅成功執行,沒有錯誤訊息,還成功印出字串
"456"
轉成數字後的相加結果。這就是弱型別的語言,這類語言偏向容忍隱性的型別轉換。雖然沒有在程式碼裡指明我想將字串
"456"
轉成數字,PHP 直譯器發現我在做算術運算,而且 "456"
可以被轉成數字,就「貼心」地自動幫忙轉成數字,然後吐出相加的結果。可能會說:這樣不是很好嗎?不用什麼事都要在語法上囉哩吧嗦指定清楚,程式自動就能「體察上意」。
在許多時候確實很方便省事,但有時真的不小心打錯程式,電腦不會知道你是故意還是不小心,一樣會自作聰明去「猜」你想做什麼,自動幫你進行你不想要的轉型。
例如下面這個例子:
$n1 = 123;
$n2 = 456;
$s1 = "Hello";
$x = $n1 + $s1;
echo $x;
執行結果:
123
PHP Warning: A non-numeric value encountered in /home/cg/root/6938116/main.php on line 4
我想做的其實是
$x = $n1 + $n2
,手誤為 $x = $n1 + $s1
,PHP 直譯器發現我在做算術運算,於是擅自將 "Hello"
硬轉型成數字,得到 0
,繼續產生執行結果 123
。而不是盡早提示錯誤、終止程式,必須等開發者自己發覺。確實,
"Hello"
根本不適合轉成數字,PHP 直譯器覺得怪怪的,幫忙顯示一個 PHP Warning 來提醒開發者。但 Warning 不等於 Error,對電腦來說,這段程式碼仍是成功運作,會繼續往下面的程式碼執行。但實際上我們得到了非預期的結果,後續執行的程式碼連帶可能也都是錯誤的結果。PHP 環境設定需要開啟 Warning 才會印出警告,開發環境通常會開啟以便 debug,生產環境通常會關閉。
弱型別最常導致 Bug 的情境,除了混用不同型別去數學運算或字串串接,另一個就是允許不同型別之間的比對。
以 PHP 為例,要比對兩個值,提供了兩種運算子:
==
:寬鬆比對,只比對兩值的內容。===
:嚴謹比對,比對兩值的型別和內容。
echo ("111" == 111) ? "Yes" : "No"; // "Yes"
echo ("111" === 111) ? "Yes" : "No"; // "No"
- 強型別(strongly typed):偏向不容許隱性型別轉換,型別檢查上較為嚴格。
- 弱型別(weakly typed):偏向容許隱性型別轉換,型別檢查上較為寬鬆。
簡單來說,就是編譯器或直譯器對型別檢查的寬容程度。
或更淺白地形容:允許編譯器或直譯器自作主張的程度。
強型別語言偏向說一是一、說二是二,你沒有在程式語法上明確指示就是沒這件事,發現不是正常寫法,直接停下來告訴你發生錯誤。
弱型別語言就不同了,發現不是正常寫法,會試圖去做一些自動轉型,讓這段程式繼續運作 下去。
至於孰優孰劣?其實沒有絕對標準。
普遍來說,強型別當然比較嚴謹,很多潛藏的 Bug 可以在編譯時期甚至撰寫時期就發現;但也代表開發過程可能需要花許多時間去雕琢語法來符合強型別的語法規範限制。
而弱型別風險相對較高,但撰寫時不會有那麼多限制。
值得一提的是,強型別和弱型別不是 1 或 0 二元論,而有「程度」的差別。
雖然 PHP 是弱型別,偏向容許隱性型別轉換,但還是有個容忍的限 度。
例如企圖將數字和陣列做算術運算,就不再是無傷大雅的 Warning,而會得到 Error:
$x = 123 + array("Apple", "Banana");
echo $x;
執行結果:
PHP Fatal error: Unsupported operand types in /home/cg/root/6938116/main.php on line 3
答案是:NO。
「靜態型別/動態型別」和「弱型別/強型別」代表不同的意義:
- 靜態型別/動態型別:變數和型別的綁定方法。
- 弱型別/強型別:語言型別系統(Type System)對型別檢查的嚴格程度,也就是