特征
通用
PHP 的类模型仅允许 单继承,而契约则通过 接口单独强制执行。一个特征可以同时提供实现和契约。具体来说,一个类可以从一个基类继承,同时使用一个或多个特征中的代码。与此同时,该类也可以实现一个或多个接口以及一个或多个特征中的契约。类使用特征不涉及任何继承层次结构,因此不相关的类可以使用相同的特征。总之,特征是一组可重用的方法和/或状态信息。
特征旨在支持类;特征不能直接实例化。
特征的成员都有 可见性,这在它们被给定类使用后适用。使用特征的类可以通过扩展或缩小可见性来更改该特征任何成员的可见性。例如,可以将私有特征成员在使用类中设为公共,并将公共特征成员在该类中设为私有。
一旦实现来自基类和一个或多个特征,可能会发生名称冲突。但是,特征使用提供了消除此类冲突的方法。从特征获取的名称也可以被赋予别名。
具有给定名称的类成员将覆盖任何该类使用的特征中具有相同名称的成员,而这又会覆盖任何来自基类的此类名称。
特征可以包含实例和静态成员,包括方法和属性。对于具有静态属性的特征,使用该特征的每个类都有该属性的实例。
特征中的方法可以完全访问使用该特征的任何类的所有成员。
特征声明
语法
trait-declaration: trait name { trait-member-declarationsopt } trait-member-declarations: trait-member-declaration trait-member-declarations trait-member-declaration trait-member-declaration: property-declaration method-declaration constructor-declaration destructor-declaration trait-use-clauses
语义
一个特征声明定义了一组命名成员,这些成员可用于使用该特征的任何类。
特征名称不区分大小写。
特征的成员是其特征成员声明子句指定的成员,以及从使用特征使用子句的任何其他特征导入的成员。
特征可以包含以下成员
- 属性 - 提供给使用该特征的类的变量。
- 方法 - 可以由使用该特征的类执行的计算和操作。
- 构造函数 - 初始化使用该特征的类的实例所需的行动。
- 析构函数 - 在不再需要使用该特征的类的实例时要执行的操作。
如果成员没有显式可见性,则假定为public
。
例子
trait T
{
private $prop1 = 1000;
protected static $prop2;
var $prop3;
public function compute( ... ) { ... }
public static function getData( ... ) { ... }
}
特征使用
语法
trait-use-clauses: trait-use-clause trait-use-clauses trait-use-clause trait-use-clause: use trait-name-list trait-use-specification trait-name-list: qualified-name trait-name-list , qualified-name trait-use-specification: ; { trait-select-and-alias-clausesopt } trait-select-and-alias-clauses: trait-select-and-alias-clause trait-select-and-alias-clauses trait-select-and-alias-clause trait-select-and-alias-clause: trait-select-insteadof-clause ; trait-alias-as-clause ; trait-select-insteadof-clause: qualified-name :: name insteadof trait-name-list trait-alias-as-clause: name as visibility-modifieropt name name as visibility-modifier nameopt
约束
特征名称列表中的名称项必须指定特征名称,不包括正在声明的特征的名称。
特征选择代替子句中的左侧名称必须明确地指定特征使用子句提供的特征的成员。特征选择代替子句中的右侧名称必须明确地指定特征使用子句提供的特征。
特征别名作为子句中的左侧名称必须明确地指定特征使用子句提供的特征的成员。特征别名作为子句中的右侧名称必须是一个新的、非限定的名称。
语义
特征使用子句可以用作特征成员声明或类成员声明的一部分,将特征的成员导入不同的特征或类。这是通过一个或多个特征使用子句项完成的,每个项都包含一个由逗号分隔的特征名称列表。特征使用子句列表以分号或花括号限定的特征选择代替子句和特征别名作为子句语句结束。
特征选择代替子句允许避免名称冲突。具体来说,左侧的名称指定要从一对名称中使用哪个名称。也就是说,T1::compute insteadof T2
;表示调用方法 compute(例如)应该由特征 T1
而不是 T2
中具有该名称的方法来满足。
特征别名作为子句允许为(可能限定的)名称分配一个简单的别名。具体来说,特征别名作为子句中的左侧名称指定要别名的特征使用子句提供的名称,而右侧的名称是别名。
如果特征别名作为子句包含可见性修饰符,如果提供了右侧名称,则修饰符控制别名的可见性,否则控制左侧名称的可见性。
例子
trait T1 { public function compute( ... ) { ... } }
trait T2 { public function compute( ... ) { ... } }
trait T3 { public function sort( ... ) { ... } }
trait T4
{
use T3;
use T1, T2
{
T1::compute insteadof T2; // disambiguate between two computes
T3::sort as private sorter; // make alias with adjusted visibility
}
}