继承与调用(重新绑定)
讨论这个问题基于这么一个场景的。
class A {
public function foo1() {
echo "AAAAA";
}
public function foo2() {
$this->foo1();
}
}
class B {
public function foo1() {
echo "BBBBB";
}
}
$obj = new B();
$obj->foo2();
现在问题是,输出"AAAAAA",还是"BBBBBB"?
我们不急着去猜。先理清楚这几个方法。
先看父类,父类有一个foo1(),有一个foo2(),foo2()调用父类自己的foo1()。如果单看父类,这是很和谐的。
再看子类,子类从父类继承了foo2(),也继承了foo1(),然后把foo1()重写了一遍。按理说,由于没重写,子类$obj->foo2()调用的还是父类的foo2(),那么foo2()里面的$this->foo1()也是父类的foo1()吧?
很遗憾,错了,foo2()里面的$this->foo1()是子类的。输出结果是“BBBBB”。因为....重新绑定了。这就是这篇文章的主题。
什么叫动态(重新)绑定呢?引用java的的思路:
子类调用父类继承下来的方法($obj->foo2()),在该方法中用$this指向了父类的某个资源(foo2()中$this->foo1()),如果这个资源没被重写,那么该方法中$this指向的是父类资源($this->foo1()的foo1()是父类定义的)。如果这个资源被重写了(子类中重新定义了foo1()),那么该方法中$this指向的是子类资源($this->foo1()是子类的)。
条件:继承,用$this->调用(this是父类引用),资源重写,还要补充一个:子类调用父类的方法,像上面的$obj->foo2()。如果子类连foo2()都重写了,
那情况也很简单,foo2()里面的this肯定调用子类自己本身的资源。现在讨论的问题是父类方法调用资源问题,不是子类方法调用资源。
重写:能继承下来,重新定义。不能继承下来的,或者没重写的,一概不算重写。(像上面,如果父类的foo1是私有,没继承下来,子类的foo1不算重
写,那么动态绑定不成立,父类foo2指向的还是父类自己的foo1)
原则:父类对子类的东西是未知的,不能想着要调用子类的任何东西。而子类对父类的认知只有能能继承下来的东西。
设计原则(记住):
父类的public , private, protected该如何分配:
1、如果父类的方法需要被子类重写,那么就不要在父类写实现,抽象出来。属性没必要重写。(用protected,可以继承下去,然后重写嘛,重绑定)
2、父类什么时候用private呢?如果该资源只打算被父类的方法访问(这个方法可以继承下去),不打算被子类方法($this)访问,那么就不需要继承下去
了,用private给父类内部用。方法一般用这个也比较多。
3、父类什么时候用protected呢?如果该资源打算给父类方法用,而且,又打算给子类方法用($this),但子类实例化不可以直接访问,那么就用protected
继承下去吧。保险些,属性一般用这个。方法就少点。
4、父类什么时候用public呢?如果像让子类对象直接在外部访问。通常是不重写的供外部直接调用的方法,比较多。
如果没重写,所以把所有东西继承下去其实也不会出什么乱子,因为指向的都是父类的资源,没重新绑定。
如果有耐心的话可以看看以下的论证例子:
<?php
class A {
protected $x =1;
private $y = 2;
public $z =3;
public function getY() {
echo 'this is A getY <br>';
return $this->y;
}
public function getZ() {
echo 'this is A getZ <br>';
return $this->z;
}
public function getX() {
echo 'this is A getX <br>';
return $this->x;
}
}
/**
class B extends A {
//$x继承下来
//$z继承下来
//所有方法继承下来
public function exe() {
echo $this->getX(); //调用A的get父类的x
echo '<br>';
echo $this->getY(); //调用A的get父类的y
echo '<br>';
echo $this->getZ(); //调用A的get父类的z
echo '<br>';
}
}
/**
resutle
this is A getX
1
this is A getY
2
this is A getZ
3
**/
/**
class B extends A {
//$x继承下来
//$z继承下来
//所有方法继承下来
public $x = 10; //继承下来,内存空间不变,覆盖了,父类方法也是调用这个
public $y = 20; //没继承下来,内存空间增加,没覆盖,有两个,父类方法不调用这个,调用父类的$y
public $z = 30; //继承下来,内存空间不变,覆盖了,父类方法也是调用这个
public function exe() {
echo $this->getX(); //调用A的get子类的x //方法继承了下来,要设置的成员也继承了下来
echo '<br>';
echo $this->getY(); //调用A的get父类的y //方法继承法了下来,要设置的成员没有继承下来
echo '<br>';
echo $this->getZ(); //调用A的get子类的z //方法继承了下来,要设置的成员也继承了下来
echo '<br>';
}
}
/**
result
this is A getX
10
this is A getY
2
this is A getZ
30
**/
/**
class B extends A {
//$x继承下来
//$z继承下来
//所有方法继承下来
public function exe() {
echo $this->getX(); //调用B中重写的getX(),x能继承下来,可以调用
echo '<br>';
echo $this->getY(); //调用B中重写的getY(),y不能继承下来,不能调用,说在B中找不到资源
echo '<br>';
echo $this->getZ(); //调用B中重写的getZ(),x能继承下来,可以调用
echo '<br>';
}
public function getY() {
echo 'this is B getY <br>';
return $this->y;
}
public function getZ() {
echo 'this is B getZ <br>';
return $this->z;
}
public function getX() {
echo 'this is B getX <br>';
return $this->x;
}
}
/**
this is B getX
1
this is B getY
Notice: Undefined property: B::$y
this is B getZ
3
**/
class B extends A {
//$x继承下来
//$z继承下来
//所有方法继承下来
public $x = 10;
public $y = 20;
public $z = 30;
public function exe() {
echo $this->getX(); //调用B中重写的getX(),x能继承下来,一个储存空间,重写,调用重写后的x
echo '<br>';
echo $this->getY(); //调用B中重写的getY(),y不能继承下来,两个储存空间,调用在B定义的y
echo '<br>';
echo $this->getZ(); //调用B中重写的getZ(),x能继承下来, 一个储存空间,重写,调用重写后的z
echo '<br>';
}
public function getY() {
echo 'this is B getY <br>';
return $this->y;
}
public function getZ() {
echo 'this is B getZ <br>';
return $this->z;
}
public function getX() {
echo 'this is B getX <br>';
return $this->x;
}
}
/**
result
this is B getX
10
this is B getY
20
this is B getZ
30
**/
$objb = new B();
$objb->exe();
?>