以下代码是否直接违反了Liskov替换原则:
子类不应破坏父类的类型定义。
class Baz {}
class Foo extends Baz {}
interface a
{
public function baz(Baz $baz);
}
class b implements a
{
public function baz(Foo $foo)
{
}
}
结果如下:
致命错误:b::baz(Foo $foo)的声明必须与a::baz(Baz $baz)兼容
你的例子是非法的,但这并不违反LSP。你的问题是定义一个接口,并期望在实现它的时候遵守契约。
重要的是要记住,LSP最终处理的是对象,而不是类。
通过实现接口a
然后尝试使方法签名不兼容,类b
的用户可能会尝试调用b::baz()
并失败,因为a::baz()
的签名需要Baz
,而您在b::baz()
上的不兼容实现需要实例Foo
。
E、 例如,如果你的提议是合法的,这可能会发生:
$baz = new Baz();
$b = new b();
// since a::baz(Baz) is specified, the class user believes this
// should be possible, but your illegal implementation
// breaks that expectation
$b->baz($baz); // Not accepted!
这根本不会破坏LSP。
正确实现接口 a
的类仍然可以接受类 Foo
和 Baz
的对象,因为可以使用子类型的对象而不是超类型的对象,正如 LSP 所说。
你不能写一个带有不兼容签名的方法,未来的协变和逆变支持也不允许这样做。
关于返回类型和参数类型中的协方差和逆差支持:PHP 7.4中提供了支持,将于2019年底发布。
你可以在这里阅读(被接受的)提案的细节。
> 仅返回类型
支持协方差(因此,如果父类或接口声明返回类型为
T,则定义现在可以指定 T
的子类)
参数类型将支持逆变(这样,如果父类或接口声明了< code>T的参数类型,子类或实现类现在可以将< code>T的超类型声明为参数类型)
在确定方法与其父级方法的兼容性时,只要新类型仍接受父级指定的类型,引擎现在就应该允许不太具体的参数类型和更具体的返回类型。换句话说:参数类型可以替换其超类型之一,返回类型可以替换子类型。
同样,这将允许LSP支持,并允许类用户能够信任他们正在编程的抽象,而不必检查他们正在使用的具体类的细节。