《Hack与HHVM权威指南》——1.5.3 属性值初始化

    xiaoxiao2024-06-03  125

    本节书摘来自华章出版社《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赋值后允许调用$this->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) { } }
    最新回复(0)