hack泛型:字符實(shí)體

2018-11-09 15:34 更新

泛型可以用于你習(xí)慣于用Hack編程的許多實(shí)體,包括類(lèi),函數(shù),方法,接口,類(lèi)型別名和特性。

Classes

考慮下面的例子,其中Stack是一個(gè)具有一個(gè)類(lèi)型參數(shù)的泛型類(lèi)T:

<?hh

namespace Hack\UserDocumentation\Generics\Entities\Examples\Classes;

class StackUnderflowException extends \Exception {}

class Stack<T> {
  private array<T> $stack;
  private int $stackPtr;

  public function __construct() {
    $this->stackPtr = 0;
    $this->stack = array();
  }

  public function push(T $value): void {
    $this->stack[$this->stackPtr++] = $value;
  }

  public function pop(): T {
    if ($this->stackPtr > 0) {
      return $this->stack[--$this->stackPtr];
    } else {
      throw new StackUnderflowException();
    }
  }
}

function useIntStack(Stack<int> $stInt): void {
  $stInt->push(10);
  $stInt->push(20);
  $stInt->push(30);
  echo 'pop => ' . $stInt->pop() . "\n";
  $stInt->push(10.5); // rejected as not being type-safe
  echo 'pop => ' . $stInt->pop() . "\n";
}

function run(): void {
  $s = new Stack();
  $s->push(5);
  useIntStack($s);
}

run();

Output

pop => 30
pop => 10.5

如圖所示,type參數(shù)T用于實(shí)例屬性聲明中$stack,作為實(shí)例方法的參數(shù)類(lèi)型push,以及作為實(shí)例方法的返回類(lèi)型pop。請(qǐng)注意,雖然push并pop使用類(lèi)型參數(shù),但它們本身不是通用方法。

該行$stInt->push(10.5);嘗試push使用非int參數(shù)進(jìn)行調(diào)用。這是被拒絕的,因?yàn)?stInt是一堆int,我們正試圖推一個(gè)float。

功能

下面是一個(gè)泛型函數(shù)的例子maxVal,有一個(gè)類(lèi)型參數(shù)T:

<?hh

namespace Hack\UserDocumentation\Generics\Entities\Examples\Functions;

function maxVal<T>(T $p1, T $p2): T {
  return $p1 > $p2 ? $p1 : $p2;
}

function run(): void {
  var_dump(maxVal(10, 20));
  var_dump(maxVal(15.6, -20.78));
  var_dump(maxVal('red', 'green'));
}

run();

Output

int(20)
float(15.6)
string(3) "red"

該函數(shù)返回傳遞給它的兩個(gè)參數(shù)中較大的一個(gè)。在調(diào)用的情況下maxVal(10, 20),如果這兩個(gè)參數(shù)的類(lèi)型都是int,則推斷為與類(lèi)型參數(shù)對(duì)應(yīng)的類(lèi)型T,并int返回一個(gè)值。在呼叫的情況下maxVal(15.6, -20.78),T被推斷為float,而在maxVal('red', 'green'),T被推斷為string。

方法

雖然push和pop方法在Stack上面的類(lèi)示例中的泛型類(lèi)中定義,但它們本身并不是泛型的。它們已經(jīng)綁定到類(lèi)類(lèi)型參數(shù)T。

就像泛型函數(shù)一樣,泛型方法也有自己的類(lèi)型參數(shù),即使該方法不屬于泛型類(lèi)??紤]庫(kù)類(lèi)型Pair:

final class Pair<Tv1, Tv2> implements ConstVector<mixed> {
  // …
  public function map<Tu>( (function(Tv): Tu) $callback ): Vector<Tu>
  public function zip<Tu>(Traversable<Tu> $iter): Vector<Pair<mixed, Tu>>
  public function zip<Tu>( Traversable<Tu> $iterable ): Vector<Pair<mixed, Tu>>
}

正如我們所看到的,方法map和zip每個(gè)方法都有一個(gè)通用參數(shù)Tu,它的類(lèi)型是從傳遞給每個(gè)方法的參數(shù)中推斷出來(lái)的。這個(gè)泛型參數(shù)意味著我們可以在方法的參數(shù)或返回類(lèi)型中使用它。請(qǐng)注意,泛型方法具有與類(lèi)不同的類(lèi)型參數(shù)(例如Tvvs Tu)。如果綁定了這些方法Tv,那么我們就不需要方法的泛型參數(shù),因?yàn)樗呀?jīng)綁定到了類(lèi)的類(lèi)型參數(shù)。

接口

像一個(gè)類(lèi)一樣,一個(gè)接口可以有類(lèi)型參數(shù); 例如:

<?hh

namespace Hack\UserDocumentation\Generics\Entities\Examples\Interfaces;

interface MyCollection<T> {
  public function put(T $item): void;
  public function get(): ?T;
}

class MyStack<T> implements MyCollection<T> {
  private Vector<T> $storage;

  public function __construct() {
    $this->storage = Vector {};
  }

  public function put(T $item): void {
    $this->storage[] = $item;
  }
  public function get(): ?T {
    // LIFO
    return $this->storage->count() > 0 ? $this->storage[0] : null;
  }
}

class MyQueue<T> implements MyCollection<T> {
  private Vector<T> $storage;

  public function __construct() {
    $this->storage = Vector {};
  }

  public function put(T $item): void {
    $this->storage[] = $item;
  }
  public function get(): ?T {
    // FIFO
    return $this->storage->count() > 0
      ? $this->storage[$this->storage->count() - 1]
      : null;
  }
}

function processCollection<T>(MyCollection<T> $p1): void {
  var_dump($p1->get());
}

function run(): void {
  $s = new MyStack();
  $s->put(5);
  $s->put(9);
  $s->put(3);
  processCollection($s);
  $q = new MyQueue();
  $q->put(5);
  $q->put(9);
  $q->put(3);
  processCollection($q);
}

run();

Output

int(5)
int(3)

在這里,我們有通用的堆棧和隊(duì)列類(lèi),每個(gè)類(lèi)都實(shí)現(xiàn)相同的通用接口,使這些類(lèi)能夠以一致的方式存儲(chǔ)和檢索元素。

Traits

像通用類(lèi)一樣,通用特征具有類(lèi)型參數(shù)列表; 例如:

trait MyTrait<T1, T2> {
  public static function f(T1 $value): void {
  // ...
}

類(lèi)型別名

類(lèi)型別名可以是任何類(lèi)型的別名,包括泛型類(lèi)型。例如:

newtype Matrix<T> = Vector<Vector<T>>;
type Serialized<T> = string;    // T is not used
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)