我有以下代码
public class A
{
public double foo(double y)
{
return real_value;
}
}
其中foo
方法-1 < y < 1
的输入和函数的结果是大于零的实数。
然后我有继承class B
继承class A
并覆盖方法foo
。
public class B extends A
{
public override double foo(double y)
{
return real_value;
}
}
其中foo
方法0 < y < 1
的输入和函数的结果是任何实数。
Liskov替换原则是否违反了?
提前致谢。
假设您想在程序中使用B作为A的子类型: 是的,您的代码明显违反了LSK。
为什么? 争论应该是逆变的。
那是什么意思? Liskov Principle确保,如果子类型B被基类型A替换,则程序的行为不变。
或者更准确地说(Barbara Liskov,1987):
“如果对于类型B的每个对象o1,则存在类型A的对象o2 这样,对于按照A定义的所有程序P,P的行为不变 当o1代替o2时,则B是A“的子类型。
例如:
class Duck { void fly(int height) {} }
class RedheadDuck : Duck { void fly(long height) {} }
class RubberDuck : Duck { void fly(short height) {} }
class LSPDemo
{
public void Main()
{
Duck p;
p = new Duck();
p.fly(int.MaxValue); // Expected behaviour
p = new RedheadDuck();
p.fly(int.MaxValue); // OK
p = new RubberDuck();
p.fly(int.MaxValue); // Fail
}
}
=> the program behaves unchanged, if the argument is contravariant.
=> e.g. base type <= sub type
=> RubberDuck violates this principle, as it does not allow all values of the base type Duck
在您的代码中,基类A foo的类型将期望参数值-1 <y <1 你的子类B foo期望参数值0 <y <1 如果您的程序将使用基类替换子类,则对于值<= 0,您的程序将不会像foo那样表现出预期的行为。
编辑:虽然你在两个foo方法的参数上使用double作为类型,但我假设你通过检查值及其范围来保护你的方法。这将导致所描述的失败,类似于示例。
P.S。:是的,这取决于你为foo定义的合同。假设您想使用B作为A的子类型,那么它违反了LSK。否则它只是一种方法覆盖。
实际上,你的重写函数违反了基函数的契约,所以Liskov原理在这里并没有用。但是,如果向基本函数添加“处理模式”之类的参数,则L原理可以正常工作(重写函数将为所有旧处理案例调用基函数)。