本节书摘来自华章出版社《hack与hhvm权威指南》一书中的第2章,第2.6节,作者 owen yamauchi,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
让我们回到关于wrapper类的引导范例。类型检查器应该接受下面的代码吗?
那么问题来了,传递一个整型的wrapper到一个期待值为num的wrapper的话,这个操作是非法的吗?看起来应该是这样的:int是num的亚型(意味着任何为int的值都会是个num),所以看起来wrapper应该同样是wrapper的亚型。
事实上,对于这个例子,类型检查器将会报告一个错误。对于类型检查器来说,我们关于int和num的亚型关系传递给wrapper和wrapper之间亚型关系的假设是不成立的。
为了说明其中的缘由,我们请思考下面的代码,函数takes_wrapper_of_num()能够做这个事情:
对于它自己来说,这是合法的:设置一个wrapper内部的值为一个float类型的值。但是如果你传递一个wrapper到上述版本的函数takes_wrapper_of_num()中,这将会导致wrapper再也不是整型。所以类型检查器不接受对函数takes_wrapper_of_num()传递wrapper。这并不是类型安全的。需要特别说明的是,这里有一项铁的规则:类型检查器并不思考函数takes_wrapper_of_num()实际上在做什么。即使函数takes_wrapper_of_num()是空的,类型检查器仍然会报告一个错误。
现在,我们看另外一段代码,类型检查器应该接受这段代码吗?
虽然这次直觉上看起来一切正常,但是类型检查器会再一次报告错误。原因是非常相似的。设想一下,我们在空白处填上如下内容:
这很明显是非法的——在main()函数执行后,任何对函数returns_wrapper_of_int()的调用都会返回某一个非int类型的wrapper。所以,再一次,对于函数returns_wrapper_of_num()里面的return语句,类型检查器会报告一个错误。
数组和集合
数组和hack的不可变集合类(immvector、immmap、immset和pair)表现上是不一致的。举例来说,它们遵从着很直观的概念,array是array的亚型。下例中对数组的使用是合法的:
类似的行为对于不可变集合类的值类型注3也是有效的。不必考虑你是使用它们自己的名字进行注解,还是用类似constvector这样的接口名字(这是推荐的)进行注解:
为什么这对于数组和不可变集合是合法的,而对于wrapper却是非法的?
就不可变集合来说,原因很简单:它们是不可变的。即使你传递一个immvector到参数为immvector的函数,这个函数也没有办法设置一个非整型值到vector中。这里没有任何方法可以破坏vector只能包含整数的规矩。
就数组而言,道理也是一样的。出于相同的目的,由于值传递语义数组和不可变集合在表象上几乎一致。在上一个示例中,从takes_array_of_num()的视角,在函数takes_array_of_int()主体内的数组实际上是只读的。函数takes_array_of_num()不能够导致数组内部具有非整型值。因为它根本就无法访问原始数组,只有复制的权限。