hack泛型介绍
某些类型(类,接口和特性)及其方法可以参数化; 也就是说,它们的声明可以有一个或多个称为类型参数的占位符名称,当实例化一个类或调用方法时,它们通过类型参数与类型相关联。具有这种占位符名称的类型或方法分别称为泛型类型或泛型方法。顶层函数也可以参数化,从而产生泛型函数。
一个泛型类的例子是Vector<T>从Hack集合实现。T被称为一个类型参数,这是使通用的。它可以保存任何类型的值,从int到类的一个实例。然而,对于类的任何实例化来说,一旦一个类型已经被关联T,就不能改变为保持任何其他类型。
<?hh
namespace Hack\UserDocumentation\Generics\Intro\Examples\Vec;
/* Signature of Vector
*
* class Vector<Tv> implements MutableCollection<Tv> {
* :
* }
*
*/
function main_vec(): void {
$x = Vector {1, 2, 3, 4}; // T is associated with int
var_dump($x);
$y = Vector {'a', 'b', 'c', 'd'}; // T is associated with string
var_dump($y);
}
main_vec();
Output
object(HH\Vector)#1 (4) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
[3]=>
int(4)
}
object(HH\Vector)#2 (4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
}
$x是一个Vector<int>,$y而是一个Vector<string>。A Vector和 Vector不是相同的类型。
方法和功能也可以是通用的。一个用例是当他们需要操纵泛型类时:
<?hh
namespace Hack\UserDocumentation\Generics\Intro\Examples\GenericMethods;
// Testing generic methods in a non-generic class.
class Box<T> {
public T $value;
public function __construct(T $v) {
$this->value = $v;
}
}
function swap<T>(Box<T> $a, Box<T> $b) : void {
$temp = $a->value;
$a->value = $b->value;
$b->value = $temp;
}
function do_int_swap(): void {
$y = new Box(3);
$z = new Box(4);
echo $y->value." ".$z->value;
swap($y, $z);
echo $y->value." ".$z->value;
}
function do_string_swap(): void {
$y = new Box('a');
$z = new Box('b');
echo $y->value." ".$z->value;
swap($y, $z);
echo $y->value." ".$z->value;
}
function doAll(): void {
do_int_swap();
do_string_swap();
}
doAll();
Output
3 44 3a bb a
上面的例子展示了一个swap<T>()在泛型Box<T>类上运行 的泛型函数。
泛型允许开发人员编写一个具有任何类型参数的类或方法,同时保持类型安全。没有泛型,完成一个类似的模型将需要创建BoxInt和BoxString类,并很快得到冗长。或者,我们可以把它$value当作一个mixed类型来进行instanceof()检查,这意味着将一个字符串插入到一个int框中不会引发类型检查错误,而只会在运行时发现。
元数
泛型类型或方法的arity是为该类型或方法声明的类型参数的数量。因此,类Vector有arity 1.哈克库通用容器类Map实现了一个有序的,字典样式的集合。这种类型有arity 2,并且使用了一个键类型和一个值类型,Map<int, Employee>例如,这个类型可以用来表示由一个整数员工编号索引的一组Employee对象。
在通用参数列表中,参数名称必须
- 是不同的
- 都以字母T开头
- 与用于封闭类,接口或特征的泛型参数不同。
在以下情况下,类Vector有一个类型参数,Tv所以有arity 1.方法map也有一个类型参数Tu,所以有arity 1。
final class Vector<Tv> implements MutableVector<Tv> {
…
public function map<Tu>((function(Tv): Tu) $callback): Vector<Tu> { … }
}
在以下的情况下,类Map
有两个类型参数,Tk
并且Tv
,因此具有元数2.方法zip
具有一个,Tu
,所以有元数1。
final class Map<Tk, Tv> implements MutableMap<Tk, Tv> {
…
public function zip<Tu>(Traversable<Tu> $iter): Map<Tk, Pair<Tv, Tu>> { … }
}
在以下情况下,函数maxValue具有一个类型参数T,因此具有参数1。
function maxValue<T>(T $p1, T $p2): T { … }