最容易混淆的知识点:
接口中的方法只能为 public
抽象方法可以为 public 和 protected,但不能为 private
如何理解:接口和抽象方法皆为方法的声明,需要让子类去实现方法体,这就要求子类必须有继承权限。同时,接口的扮演角色为对外部访问开放的一组标准,故需为 public。
实现必须以继承为前提,但重写和继承没有依赖关系。
实现的前提为继承,但重写并不需要有继承权限。你可以重写父类的 private 方法,虽然你并没有从父类继承此 private 方法,但依然受到父类的制约:参数必须完全一致。必须遵循重写时的权限可提升规则(子类重写的方法访问权限必须大于等于父类, public > protected > private)
1、Interface
接口是一个标准,是一个约定,而不是一种实现。
接口的目的是为了规范实现类。实现类需要实现接口中定义的所有方法,已实现接口标准化。
接口中的方法必须声明为 public。因为接口是一套对外开放的标准,之所以必须是 public 权限更倾向了表明对外访问控制层面,而不是子类的继承权限。
interface StudentInterface
{
public function getName();
public function setName($name);
public function getClassInfo();
}
/**
* Student 必须实现接口中声明的所有方法
*
*/
class Student implements StudentInterface
{
public $name;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getClassInfo()
{
}
}
2、抽象类与抽象方法
抽象类是由 abstract 关键字修饰的类,自身无法实例化,必须由子类继承(且当抽象类内部含有抽象方法时,子类需同时实现所有的抽象方法)。
抽象方法是由 abstract 关键之修饰的方法,只能存在于抽象类中(抽象类可以不含有抽象方法,但抽象方法必须声明在抽象类中)
<?php
//抽象类 可以不含抽象方法 但无法实例化
abstract class Foo
{
public function getFoo()
{
echo __METHOD__ . PHP_EOL;
}
}
//继承抽象类所有可继承的方法
class SubFoo extends Foo
{
}
//含有抽象方法的抽象类
abstract class Bar
{
//抽象方法声明 不能被 private 修饰 因为需要被继承实现
protected abstract function getBar();
}
//需要实现抽象父类的抽象方法
class SubBar extends Bar
{
public function getBar()
{
echo __METHOD__ . PHP_EOL;
}
}
这里要注意的还有 abstract 和 final 是对立的,abstract 要求此类必须被继承,final 则要求此类无法被继承
同时抽象方法不能修饰为 private 访问权限,因为抽象方法为声明,需被子类继承实现(注意这里我说的是实现,而不是重写,抽象方法没有函数体,没有重写一说)
3、继承与重写/继承与实现
不知道大家是不是有如下的认识,所谓重写是子类重新定义从父类继承的方法的函数体,错
重写和继承没有任何前后关系
你可以重写父类的 public/protected 方法,也可以重写父类的 private 方法(是的,你无权继承,但你依旧可以重写,但权利与义务对立统一,你可以重写的同时也收到了父类此 private 方法的制约)
实现则依赖于继承
你如果想实现父类的某方法声明时,父类可能是接口或者抽象类,则需要有继此方法的访问权限才可以。接口可能更偏向于其本质而规定所有方法声明使用 public,抽象方法则可以说完全是为了让子类继承实现,所以抽象方法必须为 public 或 protected
4、静态延迟绑定
PHP 5.3+ 的三大新特性:命名空间 自动载入 静态延迟绑定
php的继承模型中有一个存在已久的问题,那就是在父类中引用扩展类的最终状态比较困难
如何理解:“在父类中引用扩展类的最终状态比较困难” ,首先要明确的一点静态延迟绑定是针对类的属性或方法而言的,这一点并没有太多人提及,理解起来可能会认为“静态”是形容词,“延迟绑定”是主语,其实是静态属性或方法(类属性/方法)的延迟绑定。在 5.2 - 的版本中,难以在父类中定义一套静态属性和方法,使之能在被扩展类继承后,扩展类调用时可以切换为扩展类的上下文。而在 5.3+ 中, static 关键字的引入使此问题得以解决。
<?php
class Foo
{
public static $property = "foo";
// 5.2 -
public static function getPropertyBySelf()
{
echo self::$property . PHP_EOL;
}
// 5.3 + 引入了静态绑定
public static function getPropertyByStatic()
{
echo static::$property . PHP_EOL;
}
}
class Bar extends Foo
{
//注意此处重写父类属性后,内存中变分别为 Foo 和 bar 开辟了两个静态变量
public static $property = "bar";
public static function getPropertyByParent()
{
echo parent::$property . PHP_EOL;
}
}
//self将属性固话在了父类中
Bar::getPropertyBySelf(); // foo
//虽然是在父类中定义的静态方法
//但通过延迟绑定 可以切换到子类的上下文
Bar::getPropertyByStatic(); // bar
//访问父类属性
Bar::getPropertyByParent(); //foo
静态工厂
<?php
/**
* 通过 static 可以很方便的实现静态工厂类
*/
class Foo
{
// self 无法延迟绑定 固化在了 Foo 中
public static function getInstanceBySelf()
{
return new self();
}
// static 则静态延迟到调用上下文绑定
public static function getInstanceByStatic()
{
return new static();
}
}
class Bar extends Foo
{
}
var_dump(Bar::getInstanceBySelf()); //class Foo#1 (0) {}
var_dump(Bar::getInstanceByStatic()); //class Bar#1 (0) {}
可以看到 self 关键字是严格的绑定在定义类的上下文中的,而 static 在兼备 self 关键字特性的同时还具备了静态延迟绑定的特性,可以将类的属性和方法切换到调用类的上下文。