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一个函数是完全正确的num。如果你传递一个float函数就没问题了。如果你传递一个int和一个float函数,也没关系。

但是,你认为类型分析者应该接受吗?

<?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>一个函数期望一个Box<num>自从int是一个子类型num。然而,在泛型的情况下,子类型关系与其原始类型的对应关系并不一致。

原因是因为你的泛型对象是通过引用传递的,所以类型检查者没有办法安全地知道你是否正在修改Boxin addRandomToBox()以包含不是的东西num。虽然明显地向我们说,我们正在添加int中addRandomToBox(),该typechecker实际上不考虑发生了什么。所以不确定的是,返回给我们的还是一个Box<int>。

HHVM运行时并不在意,因为我们在运行时会删除泛型。

不变的集合和数组

由于不可变的集合不能被改变array(即拷贝而不是引用),泛型与上面讨论的子类型关系实际上会通过类型检查器。这是因为类型检查器可以保证实体在返回给调用者时不会被改变。

<?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)
}
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号