以下代码是否直接违反了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)兼容
答案 0 :(得分:2)
您的示例是非法的事实并不会破坏LSP。您的问题是正在定义一个接口,并且期望在实现该接口时遵守该合同。
通过实现接口a
,然后尝试使方法签名不兼容,类b
的用户可以尝试调用b::baz()
并由于a::baz()
的签名而失败期望一个Baz
,并且您在b::baz()
上的不兼容实现期望一个实例Foo
。
例如,如果您提出的建议是合法的,则可能会发生:
$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年底发布该支持。
您可以了解(接受的)提案here的详细信息。
仅对返回类型支持协方差(因此,如果父类或接口声明了返回类型为T
,则定义现在可以指定T
的子类)
参数类型将支持一致性(因此,如果父类或接口声明的参数类型为T
,则子类或实现类现在可以将T
的超类型声明为参数类型。 )
在确定方法与其父方法的兼容性时,只要新类型仍然接受父方法指定的类型,引擎现在应允许使用较少特定的参数类型和更特定的返回类型。换句话说:参数类型可以代替其父类型之一,而返回类型可以代替子类型。
再次,这将允许LSP支持,并使类用户能够信任他们针对其进行编程的抽象,而不必检查其正在使用的具体类的细节。