【问题标题】:How to define stricter typing in child class inherited methods in PHP?如何在 PHP 中的子类继承方法中定义更严格的类型?
【发布时间】:2020-07-16 08:10:10
【问题描述】:

假设我有三个这样的课程。

abstract class A {
   abstract protected function doSomething($object): array;
}

class B extends A {
   protected function doSomething(SomeObject $object): array
   {
       // something
   }
}

class C extends A {
   protected function doSomething(OtherObject $object): array
   {
       // something
   }
}

根据 PHP 的继承原则,上述结构不是合法的 PHP 代码,因为方法定义与基类不兼容。当然,我可以做类似的事情

class B extends A {
    protected function doSomething($object): array
    {
        if (!is_a($object, 'SomeObject')) {
            throw new Exception('Object passed to method is not of the correct class for this method.');
        }

        // something
    }
}

但我想知道是否有另一种解决方案来实现抽象类中的方法,该方法可以重新定义为只接受其中特定类型的对象。

【问题讨论】:

  • 您的对象可以实现相同的接口,然后您可以将该接口作为参数类型传递
  • 使子类的参数比基类参数更窄/更具体是违反 Liskov 替换原则,即如果超类 A 中的方法接受 B 类的参数并返回类 C 的对象,那么重新实现该方法的 A 的子类只能修改使得参数可以是 B 类的超类(逆变),返回类型可以是 C 类的子类(协方差)

标签: php oop solid-principles liskov-substitution-principle


【解决方案1】:

您必须以与示例类似的方式自己实现它。

PHP 支持协变和逆变is clearly defined:

协方差允许子方法返回比其父方法的返回类型更具体的类型。然而,逆变允许参数类型在子方法中的特定性低于其父方法

如果子类能够具有比其父类或接口更严格的参数要求,这将导致期望值受损。

例如:假设您有以下内容:

abstract class AbstractParam{}

class ParamOne extends AbstractParam{}
class ParamTwo extends AbstractParam{}
class ParamThree extends AbstractParam{}

abstract class AbstractService {

    abstract public function foo(AbstractParam $bar);
}

任何时候您需要使用此服务的实现,您都会确保将AbstractParam 的任何有效实例传递给foo() 都是合法的。方法签名清楚地传达了这一点。

如果我可以在继承/实现上更改它,突然间我可以将foo() 签名更改为:

public function foo(ParamOne $bar)

合约将被破坏,您无法安全地依赖AbstractService,因为不再可能将任何AbstractParam 实例传递给foo(),因此violating the LSP 在此过程中:您可以在保持正确性的同时,不再用属于该基类的子类型的对象替换基类的对象。

您问题中的方法可行,但由于上述原因,我认为这不是一个很好的设计。如果你真的必须这样做,只需从方法定义中删除类型提示:

abstract class AbstractService {

    abstract public function foo($bar);
}

class ConcreteService extends AbstractService {
    public function foo($bar) {}
}

但最好重新考虑一下你为什么真正需要它,并尝试保持类型安全。

【讨论】:

  • 实际上,如果我查看您提供的链接,我发现 7.4 允许协变和逆变。遗憾的是,服务器运行 7.0,但这仍然非常有用!
  • 很高兴您发现它很有用。无论如何,答案保持不变,即使在 7.0 上也是如此。不是说“没有办法”,而是“不应该有办法,因为它会破坏事情”:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2014-09-06
相关资源
最近更新 更多