hack泛型:子類型

2018-11-09 15:40 更新

與亞型相關的一個看似與直覺相反的領域是泛型。

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\Intuitive;

function echo_add(num $x, num $y): void {
  echo $x + $y;
}

function get_int(): int {
  return rand();
}

function run(): void {
  $num1 = get_int();
  $num2 = get_int();
  echo_add($num1, $num2); // int is a subtype of num
}

run();

Output

2229204202

由于int是一個子類型num,類型轉換器通過傳遞int一個函數(shù)是完全正確的num。如果你傳遞一個float函數(shù)就沒問題了。如果你傳遞一個int和一個float函數(shù),也沒關系。

但是,你認為類型分析者應該接受嗎?

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive;

class Box<T> {
  private Vector $box;
  public function __construct(int $firstItem) {
    $this->box = Vector {$firstItem};
  }
  public function add(T $v) {
    $this->box[] = $v;
  }
}

function addRandomToBox(Box<num> $x): void {
  $x->add(rand());
}

function createBox(): Box<int> {
  return new Box(3);
}

function run(): void {
  $box = createBox(); // we have a Box<int>
  addRandomToBox($box); // typechecker cannot guarantee a Box<int> now.
  var_dump($box); // HHVM doesn't care since we erase generics anyway.
}

run();

Output

object(Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive\Box)#2 (1) {
  ["box":"Hack\UserDocumentation\Generics\Subtypes\Examples\CounterIntuitive\Box":private]=>
  object(HH\Vector)#1 (2) {
    [0]=>
    int(3)
    [1]=>
    int(123434323)
  }
}

這似乎是應該是有效的傳遞Box<int>一個函數(shù)期望一個Box<num>自從int是一個子類型num。然而,在泛型的情況下,子類型關系與其原始類型的對應關系并不一致。

原因是因為你的泛型對象是通過引用傳遞的,所以類型檢查者沒有辦法安全地知道你是否正在修改Boxin addRandomToBox()以包含不是的東西num。雖然明顯地向我們說,我們正在添加int中addRandomToBox(),該typechecker實際上不考慮發(fā)生了什么。所以不確定的是,返回給我們的還是一個Box<int>。

HHVM運行時并不在意,因為我們在運行時會刪除泛型。

不變的集合和數(shù)組

由于不可變的集合不能被改變array(即拷貝而不是引用),泛型與上面討論的子類型關系實際上會通過類型檢查器。這是因為類型檢查器可以保證實體在返回給調(diào)用者時不會被改變。

<?hh

namespace Hack\UserDocumentation\Generics\Subtypes\Examples\Immutable;

function addRandomToArray(array<num> $x): void {
  $x[] = 3.2; // this is a copy, not a reference
}

function createArray(): array<int> {
  return array(3);
}

function run(): void {
  $arr = createArray(); // we have a array<int>
  // typechecker CAN guarantee array<int> now since what is received by
  // addRandomToArray() is a copy (passed-by-value)
  addRandomToArray($arr);
  var_dump($arr); // Still only going to contain 3, not the 3.2.
}

run();

Output

array(1) {
  [0]=>
  int(3)
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號