hack泛型:差異

2018-11-16 10:11 更新

Hack支持通用協(xié)方差和逆變。這是一個(gè)相當(dāng)先進(jìn)的話題,所以我們不會(huì)詳細(xì)討論。我們將涵蓋足夠的基礎(chǔ)知識(shí)。

每個(gè)通用參數(shù)可以選擇性地用方差指示器分別標(biāo)記:

  • + for covariance(協(xié)方差)
  • - for contravariance(逆變性)

如果沒有指示方差,參數(shù)是不變的。

協(xié)方差

如果Foo<int>是一個(gè)子類型Foo<num>,那么Foo是協(xié)變的T?!癱o”是指“與”; 并且泛型類型的子類型關(guān)系與參數(shù)的子類型關(guān)系一起變?yōu)閰f(xié)變類型參數(shù)。

這是一個(gè)協(xié)變的例子:

<?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) {
  }
}

協(xié)變類型參數(shù)用于只讀類型。因此,如果可以以某種方式設(shè)置類型,則不能使用協(xié)方差。

協(xié)方差不能用作任何方法的參數(shù)類型,或者作為該類中任何可變屬性的類型。

逆變

如果Foo<num>是一個(gè)亞型的話Foo<int>,那Foo就是逆轉(zhuǎn)了T?!癱ontra”意思是“反對(duì)”; 泛型類型的子類型關(guān)系違背了參數(shù)的子類型關(guān)系到逆變類型參數(shù)。

這是一個(gè)反例的例子:

<?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) {
  }
}

逆變類型參數(shù)用于只寫類型。因此,如果類型可以以某種方式讀取,則不能使用逆變。(例如,序列化函數(shù)是一個(gè)很好的用例)。

逆變類型參數(shù)不能用作該類中任何方法的返回類型。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)