閱讀575 返回首頁    go windows


《Hack與HHVM權威指南》——2.6 泛型和亞型

本節書摘來自華章出版社《Hack與HHVM權威指南》一書中的第2章,第2.6節,作者 Owen Yamauchi,更多章節內容可以訪問雲棲社區“華章計算機”公眾號查看。

2.6 泛型和亞型

讓我們回到關於Wrapper類的引導範例。類型檢查器應該接受下麵的代碼嗎?

function takes_wrapper_of_num(Wrapper<num> $w): void {
  // ...
}
function takes_wrapper_of_int(Wrapper<int> $w): void {
  takes_wrapper_of_num($w);
}

那麼問題來了,傳遞一個整型的wrapper到一個期待值為num的wrapper的話,這個操作是非法的嗎?看起來應該是這樣的:int是num的亞型(意味著任何為int的值都會是個num),所以看起來Wrapper應該同樣是Wrapper的亞型。
事實上,對於這個例子,類型檢查器將會報告一個錯誤。對於類型檢查器來說,我們關於int和num的亞型關係傳遞給Wrapper和Wrapper之間亞型關係的假設是不成立的。
為了說明其中的緣由,我們請思考下麵的代碼,函數takes_wrapper_of_num()能夠做這個事情:

function takes_wrapper_of_num(Wrapper<num> $w): void {
  $w->setValue(3.14159);
}

對於它自己來說,這是合法的:設置一個Wrapper內部的值為一個float類型的值。但是如果你傳遞一個Wrapper到上述版本的函數takes_wrapper_of_num()中,這將會導致wrapper再也不是整型。所以類型檢查器不接受對函數takes_wrapper_of_num()傳遞Wrapper。這並不是類型安全的。需要特別說明的是,這裏有一項鐵的規則:類型檢查器並不思考函數takes_wrapper_of_num()實際上在做什麼。即使函數takes_wrapper_of_num()是空的,類型檢查器仍然會報告一個錯誤。
現在,我們看另外一段代碼,類型檢查器應該接受這段代碼嗎?

function returns_wrapper_of_int(): Wrapper<int> {
  // ...
}
function returns_wrapper_of_num(): Wrapper<num> {
  return returns_wrapper_of_int();
}

雖然這次直覺上看起來一切正常,但是類型檢查器會再一次報告錯誤。原因是非常相似的。設想一下,我們在空白處填上如下內容:

function returns_wrapper_of_int(): Wrapper<int> {
  static $w = new Wrapper(20);
  return $w;
}
function returns_wrapper_of_num(): Wrapper<num> {
  return returns_wrapper_of_int();
}
function main(): void {
  $wrapper_of_num = returns_wrapper_of_num();
  $wrapper_of_num->setValue(2.71828);
}

這很明顯是非法的——在main()函數執行後,任何對函數returns_wrapper_of_int()的調用都會返回某一個非int類型的wrapper。所以,再一次,對於函數returns_wrapper_of_num()裏麵的return語句,類型檢查器會報告一個錯誤。
數組和集合
數組和Hack的不可變集合類(ImmVector、ImmMap、ImmSet和Pair)表現上是不一致的。舉例來說,它們遵從著很直觀的概念,array是array的亞型。下例中對數組的使用是合法的:

function takes_array_of_num(array<num> $arr): void {
  // ...
}
function takes_array_of_int(array<int> $arr): void {
  takes_array_of_num($arr);??// OK
}

類似的行為對於不可變集合類的值類型注3也是有效的。不必考慮你是使用它們自己的名字進行注解,還是用類似ConstVector這樣的接口名字(這是推薦的)進行注解:

function takes_constvector_of_num(ConstVector<num> $cv): void {
  // ...
}
function takes_constvector_of_int(ConstVector<int> $cv): void {
  takes_constvector_of_num($cv);??// OK
}
function takes_constmap_of_arraykey_mixed(ConstMap<string, mixed> $cm): void {
  // ...
}
function takes_constmap_of_string_int(ConstMap<string, int> $cm): void {
  takes_constmap_of_arraykey_mixed($cm);??// OK
}

為什麼這對於數組和不可變集合是合法的,而對於Wrapper卻是非法的?
就不可變集合來說,原因很簡單:它們是不可變的。即使你傳遞一個ImmVector到參數為ImmVector的函數,這個函數也沒有辦法設置一個非整型值到vector中。這裏沒有任何方法可以破壞vector隻能包含整數的規矩。
就數組而言,道理也是一樣的。出於相同的目的,由於值傳遞語義數組和不可變集合在表象上幾乎一致。在上一個示例中,從takes_array_of_num()的視角,在函數takes_array_of_int()主體內的數組實際上是隻讀的。函數takes_array_of_num()不能夠導致數組內部具有非整型值。因為它根本就無法訪問原始數組,隻有複製的權限。

最後更新:2017-05-26 11:31:21

  上一篇:go  《Ansible權威指南 》一1.9 本章小結
  下一篇:go  推薦 | 2016 年哪些科技切實地改變了你的生活?