泛型
泛型在hack的类型系统里面是个非常强大的特性,泛型可以允许你在不知道流程中传入的具体类型的情况下,写出类型安全的代码。一个类或者函数都可以是泛型的,这意味着它可以让调用者来选择传入的参数类型。
泛型结构体最好的例子就是数组和集合类(关于集合类的更多内容请参见第5章)。不具备明确指出数组内容具体类型的能力,它不可能推断出索引自数组的任何值的类型,并且设置数组中的一个值不能被类型检查。这些操作在php和hack中都是非常常见的,并且泛型让类型检查器能够理解并对它们进行核实。
在本章的内容中,我们将对泛型提供的所有特性进行查看和学习。
2.1 入门实例
我们将会从一个非常简单的例子入手学习:一个包含随意值的类。你可能在日常练习中从来没有这么写过这样的代码注1,但是这是一个对泛型进行介绍的最好例子。在本章的学习中,我们讲会使用它作为运行的实例。
为了使一个类“泛型化”,我们可以在类名后面放置一个由尖括号括起来的、逗号分隔的类型形参列表。一个类型形参可以简单理解为:一个用大写t开头的标识符。在泛型类定义内部,可以在变量标准中使用这些类型形参。主要在如下三个常见的位置(属性、方法形参、方法的返回标注类型)。
下面是泛型类的例子:
为了使用这种泛型类,你仅需要像往常一样对它进行实例化,然后使用得到的结果对象即可。
在这个例子中,得益于wrapper是泛型的,所以类型检查器知道$x是个整数。它能够看到你传递了整数到wrapper的构造函数中,并且推断出它应该对这个特殊的wrapper实例的使用进行类型检查,就好像把类定义中所有的tval用int进行替代一样。
在这种情况下,类型检查过程和你用下面的这个类对wrapper类进行替代得到的效果一样好。
然而,对于泛型版本,你可以对这个类使用任何类型,这具有明显的益处。如果你传递一个字符串类型到wrapper类的构造函数中,那么这个实例的getvalue()方法的返回类型也会是个字符串。你如果你传递一个?float类型的值到wrapper的构造函数中,那么该实例的getvalue()方法的返回类型也将是?float。诸如此类,你可以对能想得到的其他类型进行类似推理判断。
这就是泛型的真实力量:你可以对wrapper类写一个包含任何类型值的单独实现,但是它仍然是彻底类型安全的。
作为本次说明的最后部分,这里将介绍如何为一个泛型类的实例写一个类型标注。语法是一个类名,随后跟随着被尖括号括起来的、逗号分隔的类型标注列表。在列表中的每个标注都被称作类型实参:
类型形参(type parameter)和类型实参(type argument)的关系就和函数形参(function parameter)和函数实参(function argument)的关系一样。类型实参是泛型类定义中的类型形参在具体使用时的替代品。在本例中,函数返回值是wrapper类的一个实例,它将告诉类型检查器应该对这个对象的使用进行类型检查,就好像对该类定义中的所有tval替换为string。
2.2 其他泛型实体
类并不是唯一可以被泛型化的实体。