引入抽象类是PHP 5的一个重要变化。抽象类不能被直接实例化,抽象类中只定义(或部分实现)子类需要的方法。子类可以继承它并通过实现其中的抽象方法,使抽象类具体化。关键字abstract 定义一个抽象类。
大多数情况下,抽象类至少包含一个抽象方法。抽象方法用abstract关键字声明,其中不能有具体内容。可以像声明普通类方法那样声明抽象方法,但要以分号而不是方法体结束。
1 <?php 2 abstract class Simple 3 { 4 protected $arr = array(); 5 6 public function say() 7 { 8 echo "hello"; 9 } 10 11 abstract public function write(); 12 } 13 14 class Writer extends simple 15 { 16 public function write() 17 { 18 // .....具体实现 19 } 20 } 21 ?>
在PHP 4中,我们可以用包含警告或die()语句模拟抽象类。
1 <?php 2 class AbstractClass 3 { 4 function abstractFunction() 5 { 6 die("AbstractClass::abstractFunction() is abstract\n"); 7 } 8 } 9 ?>
这里存在一个问题,就是这个基类的抽象性只有在抽象方法调用时(即代码运行时)才能检测。在PHP 5中,抽象类在被解析时就被检测,所以更加安全。
接口
抽象类提供了具体的实现标准,而接口则是纯粹的模版。接口只能定义功能,而不包括实现的内容。接口可用关键字interface来声明。接口可以包含属性和方法的声明,但是方法体为空。
定义一个接口:
1 <?php 2 interface InterClass 3 { 4 public functon example(); 5 } 6 ?>
任何实现接口的类都要实现接口中所定义的所有方法,否则该类必须声明为abstract。一个类可以在声明中使用implements关键字来实现某个接口。
一个类可以同时继承一个父类和实现任意个接口。extends字句应该放在implements字句之前:
<?php class Consultancy extends TimedService implements Bookable, Chargeable { //... } ?>
PHP只支持继承自一个父类,因此extends关键字只能在一个类名之前。
延迟静态绑定:static 关键字
学习了抽象类和接口请看下面的代码:
1 <?php 2 abstract class DomainObject 3 {} 4 5 class User extends DomainObject 6 { 7 public static function create() 8 { 9 return new User(); 10 } 11 } 12 13 class Document extends DomainObject 14 { 15 public static function create() 16 { 17 return new Document(); 18 } 19 } 20 ?>
发现在创建这两个类的时候,存在代码重复,如果将重复代码提取到基类呢?
1 <?php 2 abstract class DomainObject 3 { 4 public static function create() 5 { 6 return new self(); 7 } 8 } 9 10 class User extends DomainObject 11 { 12 } 13 14 class Document extends DomainObject 15 { 16 } 17 18 Document :: create(); 19 ?>
可以运行下结果,报错了。。。
实际上,self对该类所起的作用与$this对对象所起的作用并不完全相同。self指的不是调用的上下文,它指的是解析上下文,因此,如果运行则报错。
在PHP 5.3中引入了延迟静态绑定的概念。该特性最明显的标志就是新关键字static。static类似于self,但它指的是表调用的类而不是包含类。
1 <?php 2 abstract class DomainObject 3 { 4 public static function create() 5 { 6 return new static(); 7 } 8 } 9 10 class User extends DomainObject 11 { 12 } 13 14 class Document extends DomainObject 15 { 16 } 17 18 var_dump(Document :: create()); 19 ?>
现在运行则通过。static不仅可以用于实例化。和self和parent一样,static还可以作为静态方法调用的标示符,甚至从非静态上下文中调用。
1 <?php 2 abstract class DomainObject 3 { 4 private $group; 5 6 public function __construct() 7 { 8 $this->group = static::getGroup(); 9 } 10 11 public static function create() 12 { 13 return new static(); 14 } 15 16 static function getGroup() 17 { 18 return 'Default'; 19 } 20 } 21 22 class User extends DomainObject 23 { 24 } 25 26 class Document extends DomainObject 27 { 28 static function getGroup() 29 { 30 return "document"; 31 } 32 } 33 34 class SpreadSheet extends Document 35 { 36 } 37 38 print_r(User::create()); 39 print_r(SpreadSheet::create()); 40 ?>
运行看结果,PHP5.3之前self只能查找DomainObject类中的getGroup()。