泛型
泛型在hack的類型系統裡面是個非常強大的特性,泛型可以允許你在不知道流程中傳入的具體類型的情況下,寫出類型安全的代碼。一個類或者函數都可以是泛型的,這意味着它可以讓調用者來選擇傳入的參數類型。
泛型結構體最好的例子就是數組和集合類(關于集合類的更多内容請參見第5章)。不具備明确指出數組内容具體類型的能力,它不可能推斷出索引自數組的任何值的類型,并且設定數組中的一個值不能被類型檢查。這些操作在php和hack中都是非常常見的,并且泛型讓類型檢查器能夠了解并對它們進行核實。
在本章的内容中,我們将對泛型提供的所有特性進行檢視和學習。
2.1 入門執行個體
我們将會從一個非常簡單的例子入手學習:一個包含随意值的類。你可能在日常練習中從來沒有這麼寫過這樣的代碼注1,但是這是一個對泛型進行介紹的最好例子。在本章的學習中,我們講會使用它作為運作的執行個體。
為了使一個類“泛型化”,我們可以在類名後面放置一個由尖括号括起來的、逗号分隔的類型形參清單。一個類型形參可以簡單了解為:一個用大寫t開頭的辨別符。在泛型類定義内部,可以在變量标準中使用這些類型形參。主要在如下三個常見的位置(屬性、方法形參、方法的傳回标注類型)。
下面是泛型類的例子:
為了使用這種泛型類,你僅需要像往常一樣對它進行執行個體化,然後使用得到的結果對象即可。
在這個例子中,得益于wrapper是泛型的,是以類型檢查器知道$x是個整數。它能夠看到你傳遞了整數到wrapper的構造函數中,并且推斷出它應該對這個特殊的wrapper執行個體的使用進行類型檢查,就好像把類定義中所有的tval用int進行替代一樣。
在這種情況下,類型檢查過程和你用下面的這個類對wrapper類進行替代得到的效果一樣好。
然而,對于泛型版本,你可以對這個類使用任何類型,這具有明顯的益處。如果你傳遞一個字元串類型到wrapper類的構造函數中,那麼這個執行個體的getvalue()方法的傳回類型也會是個字元串。你如果你傳遞一個?float類型的值到wrapper的構造函數中,那麼該執行個體的getvalue()方法的傳回類型也将是?float。諸如此類,你可以對能想得到的其他類型進行類似推理判斷。
這就是泛型的真實力量:你可以對wrapper類寫一個包含任何類型值的單獨實作,但是它仍然是徹底類型安全的。
作為本次說明的最後部分,這裡将介紹如何為一個泛型類的執行個體寫一個類型标注。文法是一個類名,随後跟随着被尖括号括起來的、逗号分隔的類型标注清單。在清單中的每個标注都被稱作類型實參:
類型形參(type parameter)和類型實參(type argument)的關系就和函數形參(function parameter)和函數實參(function argument)的關系一樣。類型實參是泛型類定義中的類型形參在具體使用時的替代品。在本例中,函數傳回值是wrapper類的一個執行個體,它将告訴類型檢查器應該對這個對象的使用進行類型檢查,就好像對該類定義中的所有tval替換為string。
2.2 其他泛型實體
類并不是唯一可以被泛型化的實體。