hack泛型:约束条件
hack泛型类型约束条件表示为了被接受为给定类型参数的类型参数,类型必须满足的要求。(例如,它可能必须是给定的类类型或该类类型的子类型,或者可能必须实现给定的接口。
hack泛型类型约束有两种,分别由关键字as和super关键字指定。每个都在下面讨论。
通过as指定约束
考虑下面的例子,其中类Complex有一个类型参数T,并且有一个约束num:
<?hh
namespace Hack\UserDocumentation\Generics\Constraints\Examples\Constraint;
class Complex<T as num> {
private T $real;
private T $imag;
public function __construct(T $real, T $imag) {
$this->real = $real;
$this->imag = $imag;
}
public static function add(Complex<T> $z1, Complex<T> $z2): Complex<num> {
return new Complex($z1->real + $z2->real, $z1->imag + $z2->imag);
}
public function __toString(): string {
if ($this->imag === 0.0) {
// Make sure to cast the floating-point numbers to a string.
return (string) $this->real;
} else if ($this->real === 0.0) {
return (string) $this->imag . 'i';
} else {
return (string) $this->real . ' + ' . (string) $this->imag . 'i';
}
}
}
function run(): void {
$c1 = new Complex(10.5, 5.67);
$c2 = new Complex(4, 5);
// You can add one complex that takes a float and one that takes an int.
echo "\$c1 + \$c2 = " . Complex::add($c1, $c2) . "\n";
$c3 = new Complex(5, 6);
$c4 = new Complex(9, 11);
echo "\$c3 + \$c4 = " . Complex::add($c3, $c4) . "\n";
}
run();
Output
$c1 + $c2 = 14.5 + 10.67i
$c3 + $c4 = 14 + 17i
没有这个as num约束,会报告一些错误,包括以下内容:
- return方法中的语句add对未知类型的值执行算术运算T,但是对于所有可能的类型参数没有定义算术。
- if方法中的语句__toString将未知类型的值T与a float进行比较,但是没有为所有可能的类型参数定义这样的比较。
- return方法中的语句会__toString取消未知类型的值T,但是对于所有可能的类型参数都没有定义这样的操作。同样,一个未知类型的值T正在与一个字符串连接。
该run()代码创建float并int分别情况下,类的Complex。
总之,T as U断言T必须是一个子类型U。
通过。指定约束 super
与as类型约束不同,T super U断言T必须是超类型的U。
这种约束相当奇特,但解决了多种类型“碰撞”时遇到的一个有趣的问题。下面是一个如何concat在库接口类型中使用方法的例子ConstVector:
interface ConstVector<+T> {
public function concat<Tu super T>(ConstVector<Tu> $x): ConstVector<Tu>;
// ...
}
考虑我们调用concat
a Vector<float>
和a 连接的情况Vector<int>
。由于它们有一个共同的超类型,num
所以这个super
约束允许检查器确定这num
是推断的类型Tu
。