关于LSP(Liskov替换原则)和子类型的问题

时间:2011-03-11 10:56:46

标签: oop programming-languages types logic liskov-substitution-principle

LSP

  

如果q(x)是关于类型T的对象x可证明的属性,那么对于类型S的对象y,q(y)应该为真,其中S是T的子类型。

我可以改写如下:

  对于T =>的任何x,q(x)为真。 q(y)对于T

的任何子类型的任何y都为真

那么另一个声明呢?

  对于T的任何x,q(x)为真,对于S =>的任何y,q(y)为真。 S是T

的子类型

有意义吗?我们可以将其用作subtype定义吗?

3 个答案:

答案 0 :(得分:3)

q(x) is true for any x of T and q(y) is true for any y of S => S is a subtype of T

答案是。表达式的含义是可以定义S和T的共同超类型R,然后LSP(对该名称如何成为主流的耻辱)将适用于T-> R和S-> R。

在打字理论中,存在包含语义的类型,并且存在遵循语义的类型的实现,可能通过继承实现。

实际上,指定类型语义(q(x)部分)的唯一合理方法是通过实现,因此我们留下了接口形式的无语义签名,和继承用于实现目的,并实现他们喜欢的接口,无法检查它们是否正确执行。

研究已经尝试定义形式语言来指定类型,因此工具可以检查实现是否遵循类型定义,但是工作量太大以至于它可以很好地编译正式语言到可执行代码。这是 Catch-22 的情况,我认为永远不会解决。

回到原来的问题,在今天被称为“Duck Typing”的语言中,答案是不可判定的,因为任何类型的对象都可以传递给任何函数,如果正确的签名是正确的,那么输入是正确的。实施,结果是正确的。让我解释一下......

使用Eiffel之类的语言,您可以在List.append()上设置后置条件,List.length()必须在操作后增加{{1}}。这不是像Perl,JavaScript,Python甚至Java这样的语言的工作方式。缺乏类型严格性允许比更严格的类型定义更简洁的代码。

答案 1 :(得分:2)

没有意义;使用and的语句在S和T中是对称的。 但我想你想说的是以下

  

如果对于任何命题q都是这样的情况,q(x)对于类型x的所有T是可证明的,那么对于所有y:类型,q(y)也是可证明的S,我们可能会考虑S T的子类型。

我更喜欢使用数学逻辑而不是非正式的英语,但如果我的定义正确,那就是行为子类型,这些日子通常被称为“鸭子打字”。这是一个非常好的子类型原则,并且再次导致这样的想法:在任何需要类型T的值的上下文中,您可以提供类型为S的值,并且它可以,因为类型的值S保证满足上下文所期望的所有属性。

答案 2 :(得分:1)

我认为不,你不能用它作为定义。此外,如果q(x)对于任何x的T都为真,则q(y)对于S的任何y都为真 它也可能意味着T是S的子类型。

要确定哪个子类型是哪个(假设您知道它们之间存在继承关系),您还必须知道哪些更“通用” 或者哪个比另一个更“专业化”。