访问控制与继承

[TOC]

本文验证剖析失去的论断

  • 类内调用,不受访问控制束缚限度。类间调用,则受访问控制束缚限度。
  • 只有 public 润饰的成员,能力在类外被拜访。即实例化对象只能够间接拜访被 public 润饰的成员。(留神:当类中申明魔术办法 __get (), 实例化对象拜访类未定义或公有的成员是间接拜访。)
  • 子类与父类中同时定义一个同名的办法。

    • 父类中的同名办法被 private 润饰,子类中的同名办法与父类的同名办法,不形成重写(overriding)关系。并且①如果父类中有其余私有办法调用此同名办法②子类不重写调用此同名办法的私有办法,那么子类的实例化对象拜访这个私有办法,私有办法外面调用的还是父类的同名办法。(其余文章参考:从父类继承的函数中的$ this依然指向php中的父对象?)
    • 父类中的同名办法被 public 或 protected 润饰,子类中的同名办法与父类的同名办法,形成重写(overriding)关系,但要求润饰子类的同名办法的拜访修饰符的作用范畴 >= 润饰子类的同名办法的拜访修饰符的作用范畴,否则报致命谬误。个别代码编辑器也会提醒。
  • $this 是一个伪变量,示意对主叫对象的援用。当一个办法在类定义外部被调用时,可应用 $this。
  • __CLASS__ 魔术常量,示意以后类。作用与 get_class() 雷同,要想获取主叫对象的所属类的类名,可应用 get_called_class() 函数, 或者 get_class($this) 。

一、访问控制

类中的成员(属性或类常量或办法)前无任何拜访修饰符润饰时,默认被 pubic 润饰。
访问控制修饰符作用范畴是否参加继承
public以后类外部、子类外部、以后类内部、子类内部YES
protected以后类外部、子类外部YES
private只能在以后类外部被拜访NO
留神:
只有参加继承(被 public 或 protected 润饰)的成员办法,才能够 overriding(重写)。因为 private 润饰的公有办法对于子类来说,是不可见的,即***简直能够认为公有办法不参加继承***。此外,php 中的 (overload)重载,并不是真正意义上的、像其余语言(如java)所形容的那种概念。 PHP 重载。

示例代码

上面的代码写在名为 base.php 的文件中,运行环境 PHP 8。

<?phpclass Super{    public $name; //姓名    public $gender; //性别    protected $birth; //出生日期    private $telephone; //手机号码    // 构造方法    public function __construct($name, $gender, $birth, $telephone)    {        $this->name = $name;        $this->gender = $gender;        $this->birth = $birth;        $this->telephone = $telephone;    }    // 公有办法    private function printHello() {        echo __CLASS__ . ' hello' . PHP_EOL;    }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        $this->printHello();    }}class child extends Super{    // 申明一个私有的与父类中公有办法 printHello 同名的办法 printHello,看是否形成对的父类中的同名办法的重写    private function printHello() {        echo "阿凡提de小毛驴";    }}$init = ['绘梨衣', '女', '2003-08-12', '158xxxx0812'];$super = new Super(...$init);$child = new child(...$init);echo $super->printTest();echo $child->printTest();

运行下面的代码,后果输入:

[Running] php "base.php"Super helloSuper hello[Done] exited with code=0 in 0.086 seconds

重点剖析

将下面的代码精简一下, 重点剖析子类继承父类的私有或受爱护的办法中,调用父类的公有办法,而后又在子类中申明同名的公有办法, $this 援用的疑难

先论述一下我的思路:

先申明下,我理解访问控制的基本概念,晓得被 private 润饰的办法只能在本类调用。故而,我认为,当子类继承父类时,子类领有了父类全副的非公有办法,即父类全副的非公有办法都存在于子类(其实子类继承了父类的全副办法,只是父类公有办法不能被子类拜访)。基于父类公有办法不能被子类拜访,而子类有继承了父类的私有办法。因而,当父类的非公有办法中存在调用父类公有办法,且父类被继承后。我认为是会报错的,因此须要从新申明一个与父类公有办法同名的办法(这个从新申明的办法能够被任意的拜访修饰符润饰,并且咱们晓得,这并不形成重写关系)。`而后,问题来了。子类中不须要从新申明与父类公有办法的同名办法,因为他不会报错,从新申明了这个与父类公有办法的同名办法也没用,调用继承的非公有办法,外面代码执行的还是父类的公有办法,而非在子类内从新申明的与父类公有办法同名的办法。

下面的形容可能太罗嗦了,间接看代码吧。我将把它分为四种状况去探讨。

第一种状况:子类间接继承父类,子类外部不实现任何办法。

<?phpclass Super{    // 公有办法 printHello    private function printHello() {        echo get_called_class() . ' hello' . PHP_EOL;    }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        var_dump(get_class($this));        var_dump(get_class_methods($this));        $this->printHello();    }}class child extends Super{     }$super = new Super();echo $super->printTest();echo '------------------------------------'.PHP_EOL;$child = new child();echo $child->printTest();

输入的后果如下。从输入后果来看,咱们晓得子类继承了父类的全副办法,包含 Super 公有的办法 printHello()。显然这并不合乎咱们的预期,因为在其余强类型面向对象语言中,private 润饰的办法被形容为不可继承的。这里咱们先能够认为这是因为 php 底层实现导致的起因,先不予考虑,也简直认为在 php 中,公有办法不能被继承。对这里有疑难的同学可先参考这篇文章,PHP内核摸索:继承,多态与抽象类。

基于这样的思考,我认为子类中只存在一个私有办法 printTest(),在类外,Child 的实例化的对象 $child 在调用 printTest() 时,会因为 Child 中没有可拜访的 printHello() 办法,收回 Fatal Error。然而,和我意料的不统一。$child->printTest() 时,printTest() 外部的 $this->printHello() 拜访的是父类的公有办法 printHello(),且此时的 $this 是 $child 对象的援用。

这不禁让人纳闷,子类的实例化对象的援用 $this 怎么能够拜访父类的公有办法?这不迷信。

string(5) "Super"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}Super hello------------------------------------string(5) "child"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}child hello

第二种状况:子类申明一个与父类公有办法同名的 printHello() 办法

留神:此办法并不与父类中的 printHello() 形成重写关系。

<?phpclass Super{    // 公有办法 printHello    private function printHello() {        echo get_called_class() . ' hello' . PHP_EOL;    }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        var_dump(get_class($this));        var_dump(get_class_methods($this));        $this->printHello();    }}class child extends Super{     public function printHello() {        echo "阿凡提de小毛驴";    }}$super = new Super();echo $super->printTest();echo '------------------------------------'.PHP_EOL;$child = new child();echo $child->printTest();

后果输入如下。从后果能够看出,$child->printTest() 时,此时的 $this 是 $child 对象的援用,printTest() 外部的 $this->printHello() 拜访的仍然是父类的公有办法 printHello(),而非子类中申明的 printHello()。这又让人更加纳闷了。

string(5) "Super"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}Super hello------------------------------------string(5) "child"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}child hello

第三种状况:子类重写父类私有办法 printTest() ,但函数体不变。

<?phpclass Super{    // 公有办法 printHello    private function printHello() {        echo get_called_class() . ' hello' . PHP_EOL;    }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        var_dump(get_class($this));        var_dump(get_class_methods($this));        $this->printHello();    }}class child extends Super{     public function printTest() {        $this->printHello();    }}

后果输入如下。此时的后果是合乎预期的,$child->printTest() 是产生致命谬误。因为 Child 中并没有能够拜访的 printHello() 办法。但即便这样,比照第一种状况,也呈现了新的疑难?子类中重写的 printTest() 与 继承的 printTest() 实现上是一样的,为什么第一种代码不报错,而以后的代码会报错,是因为继承的 printTest() 和 重写的 printTest() 在内存中存储的地位不一样吗?

string(5) "Super"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}Super hello------------------------------------Fatal error: Uncaught Error: Call to private method Super::printHello() from context 'child' on line 26Error: Call to private method Super::printHello() from context 'child'

第四种状况:子类申明一个与父类公有办法同名的 printHello() 办法,并且子类重写父类私有办法 printTest() ,但函数体不变。

<?phpclass Super{    // 公有办法 printHello    private function printHello() {        echo get_called_class() . ' hello' . PHP_EOL;    }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        var_dump(get_class($this));        var_dump(get_class_methods($this));        $this->printHello();    }}class child extends Super{     // public function printHello() {    //     echo "阿凡提de小毛驴";    // }    // 申明一个私有办法,调用下面的公有办法 printHello    public function printTest() {        $this->printHello();    }}$super = new Super();echo $super->printTest();echo '------------------------------------'.PHP_EOL;$child = new child();echo $child->printTest();

此时后果输入如下。我想此种状况是最没有疑难的,也是合乎预期的。

string(5) "Super"array(2) {  [0] =>  string(10) "printHello"  [1] =>  string(9) "printTest"}Super hello------------------------------------阿凡提de小毛驴