《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()不能够导致数组内部具有非整型值。因为它根本就无法访问原始数组,只有复制的权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值