9

我知道我不能在 PHP 中重载方法。而且,据我所知,private类中的方法对于扩展基类的类是不可见的。那么为什么这不起作用呢?

class Base {
  private function foo($arg) {
     print "Base $arg";
  }
}

class Child extends Base {
  public function foo() {
     print "Child";
  }
}

$c = new Child;
print $c->foo();

错误:

PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17

我假设该foo($arg)方法在Child课堂上是不可见的,因为 is private。所以,我没有重载foo,我只是创建了一个名为foo.

4

2 回答 2

3

您可以使用 __call 函数在 PHP 中进行函数重载:http ://www.php.net/manual/en/language.oop5.overloading.php#object.call

除此之外,您的问题是,您违反了可替代性原则: http ://en.wikipedia.org/wiki/Liskov_substitution_principle

PHP 使用的东西。这样,如果您将 Base 类类型的对象替换为 Child 类类型的对象,则违反了可替换性。您在派生类中更改基类的接口,删除方法 foo(...) 的参数,这样,在不破坏程序的情况下,不能用子类类型的对象替换基类类型的对象,因此违反了 Liskov 的可替代性原则 (LSP)。

于 2013-08-17T12:58:53.810 回答
3

要修复通知,只需foo()将 Child 更改为

public function foo($arg = null) {

至于“为什么这不起作用”的问题:

PHP 中的可见性严格来说是关于运行时访问。它不会影响您如何扩展/组合/重载类和方法。从子类型中的超类型中放松私有方法的可见性将在子类型中添加一个单独的方法,而无法访问超类型中的相同命名方法。但是,PHP 会为这些假定父子关系。但这并没有引起通知。至少,不是靠它自己。

您收到通知的原因是您还试图更改方法签名。您foo()不再需要$arg传递给它。当您假设方法之间存在父子关系时,这是一个问题,因为Liskov 替换原则指出“如果 S 是 T 的子类型,则 T 类型的对象可以用 S 类型的对象替换”而不破坏程序. 换句话说:如果您有使用 的代码Base,您应该能够替换Base为,Child并且程序应该仍然像使用Base.

假设您Base也有一个公共方法bar()

class SomeClientUsingBase
{
    public function doSomethingWithBase(Base $base)
    {
        $result = $base->bar();
        // …

现在想象一下需要参数的Child更改。bar()如果您随后将Childfor传递给Base客户端,您将破坏客户端,因为客户端在$base->bar();没有参数的情况下调用。

显然,您可以更改客户端以传递参数,但是代码实际上取决于Child方法的定义方式,因此 Typehint 是错误的。事实上,Child不是 a Basethen,因为它的行为不像 a Base。那就断了继承。

现在有趣的是,如果你从 中删除它,$argfoo()技术上讲,你并没有违反 LSP,因为客户端仍然可以工作。通知在这里是错误的。调用$base->foo(42)以前使用的客户端Base仍然可以使用 a Child,因为Child可以简单地忽略该参数。但是 PHP 希望您将参数设为可选。

请注意,LSP 也适用于方法可能返回的内容。PHP 只是没有在签名中包含返回类型,因此您必须自己考虑到这一点。您的方法必须返回超类型返回的内容或行为等效的内容。

于 2013-08-17T14:39:02.260 回答