hack泛型:差异

2018-11-16 10:11 更新

Hack支持通用协方差和逆变。这是一个相当先进的话题,所以我们不会详细讨论。我们将涵盖足够的基础知识。

每个通用参数可以选择性地用方差指示器分别标记:

  • + for covariance(协方差)
  • - for contravariance(逆变性)

如果没有指示方差,参数是不变的。

协方差

如果Foo<int>是一个子类型Foo<num>,那么Foo是协变的T。“co”是指“与”; 并且泛型类型的子类型关系与参数的子类型关系一起变为协变类型参数。

这是一个协变的例子:

<?hh

namespace Hack\UserDocumentation\Generics\Variance\Examples\Covariance;

// This class is readonly. Had we put in a setter for $this->t, we could not
// use covariance. e.g., if we had function setMe(T $x), you would get this
// cov.php:9:25,25: Illegal usage of a covariant type parameter (Typing[4120])
//   cov.php:7:10,10: This is where the parameter was declared as covariant (+)
//   cov.php:9:25,25: Function parameters are contravariant
class C<+T> {
  public function __construct(private T $t) {}
}

class Animal {}
class Cat extends Animal {}

function f(C<Animal> $p1): void { var_dump($p1); }

function g(array<Animal> $p1): void { var_dump($p1); }

function run(): void {
  f(new C(new Animal()));
  f(new C(new Cat()));  // accepted

  g(array(new Animal(), new Animal()));
  g(array(new Cat(), new Cat(), new Animal())); // arrays are covariant
}

run();

Output

object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\C)#1 (1) {
  ["t":"Hack\UserDocumentation\Generics\Variance\Examples\Covariance\C":private]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Animal)#2 (0) {
  }
}
object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\C)#1 (1) {
  ["t":"Hack\UserDocumentation\Generics\Variance\Examples\Covariance\C":private]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Cat)#2 (0) {
  }
}
array(2) {
  [0]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Animal)#1 (0) {
  }
  [1]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Animal)#2 (0) {
  }
}
array(3) {
  [0]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Cat)#2 (0) {
  }
  [1]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Cat)#3 (0) {
  }
  [2]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Covariance\Animal)#4 (0) {
  }
}

协变类型参数用于只读类型。因此,如果可以以某种方式设置类型,则不能使用协方差。

协方差不能用作任何方法的参数类型,或者作为该类中任何可变属性的类型。

逆变

如果Foo<num>是一个亚型的话Foo<int>,那Foo就是逆转了T。“contra”意思是“反对”; 泛型类型的子类型关系违背了参数的子类型关系到逆变类型参数。

这是一个反例的例子:

<?hh

namespace Hack\UserDocumentation\Generics\Variance\Examples\Contravariance;

// This class is write only. Had we put in a getter for $this->t, we could not
// use contravariance. e.g., if we had function getMe(T $x): T, you would get
// con.php:10:28,28: Illegal usage of a contravariant type
//                   parameter (Typing[4121])
//  con.php:5:10,10: This is where the parameter was declared as
//                   contravariant (-)
//  con.php:10:28,28: Function return types are covariant
class C<-T> {
  public function __construct(private T $t) {}
  public function setMe(T $val): void {
    $this->t = $val;
  }
}

class Animal {}
class Cat extends Animal {}

function main(): void {
  $animal = new Animal();
  $cat = new Cat();
  $c = new C($cat);
  // calling setMe with Animal on an instance of C that was initialized with Cat
  $c->setMe($animal);
  var_dump($c);
}

main();

Output

object(Hack\UserDocumentation\Generics\Variance\Examples\Contravariance\C)#3 (1) {
  ["t":"Hack\UserDocumentation\Generics\Variance\Examples\Contravariance\C":private]=>
  object(Hack\UserDocumentation\Generics\Variance\Examples\Contravariance\Animal)#1 (0) {
  }
}

逆变类型参数用于只写类型。因此,如果类型可以以某种方式读取,则不能使用逆变。(例如,序列化函数是一个很好的用例)。

逆变类型参数不能用作该类中任何方法的返回类型。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号