《Hack與HHVM權威指南》——1.5.3屬性值初始化

華章計算機發表於2017-05-02

本節書摘來自華章出版社《Hack與HHVM權威指南》一書中的第1章,第1.5.3節,作者 Owen Yamauchi,更多章節內容可以訪問雲棲社群“華章計算機”公眾號檢視。

1.5.3 屬性值初始化

為了維護型別安全,型別標註過的屬性在初始化時,無論是嚴格模式還是區域性模式,型別檢查器會強加一些規則。首要目標就是確保屬性值在沒有初始化為正確型別的值之前,不能被讀取。
對於靜態的屬性值,規則非常簡單:任何不可為空的(non-nullable)的靜態屬性值都必須有一個初始化值。沒有顯式初始值的任何可為空的(nullable)屬性值將會被隱性地初始化為null。
非靜態的屬性值將會有一套更加複雜的規則。型別檢查器將確保絕對不會對一個擁有未被初始化的不可為空的屬性值的物件進行例項化。為達到這個目的,任何不可為空、非靜態的屬性值如果沒有初始化值的話,都必須在類的建構函式中進行初始化:

class Person {
  private string $name;
  private ?string $address;
  public function __construct(string $name) {
    $this->name = $name;
  } 
}

這個程式碼將會通過型別檢查器的檢查:屬性$name已經被恰當地初始化了,並且$address也是可為空的,那麼它不需要被初始化。
型別檢查器將會確保在建構函式中的所有程式碼分支下,所有的屬性值都被初始化。下面的程式碼:

class Person {
  private string $name;
public function __construct(string $name, bool $skip_name) {
    if (!$skip_name) {
      $this->name = $name;
    }
  } 
}

型別檢查器將會報告一個錯誤:

/home/oyamauchi/test.php:5:19,29: The class member name is not always properly
initialized
Make sure you systematically set $this->name when the method __construct is
called
Alternatively, you can define the type as optional (?...)
 (NastCheck[3015])

對於型別檢查器的這條規則,另外一個組成部分是,在建構函式所有的屬性被初始化之前,不允許呼叫任何公共的或者受保護的方法。下面的程式碼:

class C {
  private string $name;
  public function __construct(string $name) {
    $this->doSomething();
    $this->name = $name;
  }
  protected function doSomething(): void {
     // ...
  }
}

型別檢查器將會丟擲一個錯誤(不管怎樣,你都會被允許在$this->name賦值之後呼叫$this->doSomething()方法 ):

/home/oyamauchi/test.php:6:14,18: Until the initialization of $this is over, 
you can only call private methods
The initialization is not over because $this->name can still potentially be 
null (NastCheck[3004])

在這種情況下,允許呼叫私有方法,但是你所呼叫的每個私有方法都會被嚴格檢查,以避免訪問到沒有被初始化的屬性值。非私有的方法不能夠通過這種方法進行檢查,因為它們可能在子類之中被覆蓋,所以在這種情形下,對非私有方法的呼叫是非法的。請參考下面的程式碼:

class C {
  private string $name;
  public function __construct(string $name) {
    $this->dumpInfo();
    $this->name = $name;
  }
  private function dumpInfo(): void {
    var_dump($this->name);
  } 
}

型別檢查器將會丟擲這個錯誤(再說一次,在$this->name賦值後允許呼叫$th
is->dumpInfo()方法):

/home/oyamauchi/test.php:11:21,24: Read access to $this->name before
initialization (Typing[4083])

在抽象類中宣告的屬性具有規則豁免權。不管怎樣,具體的子類進行初始化的時候都會要求初始化它的祖先類未初始化的屬性值。請看下面的程式碼:

abstract class Abstr {
  protected string $name; 
} 
class C extends Abstr {
}

型別檢查器將會丟擲如下錯誤:

/home/oyamauchi/test.php:5:7,7: The class member name is not always properly
initialized
Make sure you systematically set $this->name when the method __construct is
called
Alternatively, you can define the type as optional (?...)
(NastCheck[3015])

最後,對於本節中的例子,屬性值作為建構函式的一個引數進行初始化,你可以使用建構函式引數升級(詳情請見3.5節的內容)。它避免了公式化的程式碼,而且你根本不必對屬性初始化的問題思考什麼事情:

class C {
  public function __construct(private string $name) { } 
}


相關文章