接口/抽象类/方法实现与重写/静态延迟绑定的一些知识点的梳理(PHP实例)

原创
2016/09/01 11:48
阅读数 348

最容易混淆的知识点:

接口中的方法只能为 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 关键字特性的同时还具备了静态延迟绑定的特性,可以将类的属性和方法切换到调用类的上下文。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部