Liskov Substitution Principle声明
如果
S
是T
的子类型,则T
类型的对象可以替换为S
类型的对象,而不会更改该程序的任何所需属性
但是,在Scala中,PartialFunction
在所有情况下都不适用/定义。
trait Function1 {
def apply(x: A): R
}
trait PartialFunction extends Function1 {
def apply(x: A): R
def isDefinedAt(x: A): Boolean
}
如果将PartialFunction
应用于未定义的值,您将收到异常。
在scala中创建PartialFunction
的便捷方法是使用模式匹配。这样做,您会收到MatchError
未定义的值。
val fn:Function1[String, Int] = s => s.length
val pf:PartialFunction[String, Int] = {
case "one" => 3
}
def program(f:Function1[String, Int], s:String):(Boolean, Int) = (
f.isInstanceOf[Function1[String, Int]], f(s)
)
program(fn, "one") == program(pf, "one")
program(fn, "two") == program(pf, "two")
fn:String => Int =< function1>
pf:PartialFunction [String,Int] =< function1>
program:program [](val f:String => Int,val s:String)=> (Boolean,Int)
res0:Boolean = true
scala.MatchError:两个(类java.lang.String)
at scala.PartialFunction $$ anon $ 1.apply(delme.sc:249)
at scala.PartialFunction $$ anon $ 1.apply(delme.sc:247)
at ...
fn
和pf
都是Function1
的子类型,但我不能在fn
替换pf
而不改变program
。所以在我看来这违反了LSP。
您怎么看?
答案 0 :(得分:4)
fn
和pf
不是Function1
的子类型,因为它们根本不是类型。它们是值,LSP并不表示您可以使用T
类型的任何对象,并将其替换为S
类型的任何对象:Int
肯定是{{Int
的子类型1}},但用1
替换2
会使正确的程序不正确。
PartialFunction[String, Int]
是Function1[String, Int]
的子类型,但Function1
的合同并不禁止apply
抛出异常,事实上明确允许它:
将此函数的主体应用于参数。 可能会引发异常。
因此,如果您的程序可以处理Function1[A, B]
类型的对象,则必须以某种方式处理apply
抛出异常,而PartialFunction
抛出MatchError
只是一个具体案例。
答案 1 :(得分:0)
根据维基百科,LSP需要附加条款。
子类的方法不应抛出新的异常,除非这些异常本身是超类型方法抛出的异常的子类型。
所以(假设维基百科是正确的),没有PartialFunction不会违反LSP,因为它不适用于这种情况。
编辑:scaladoc中还有其他信息:( Scaladoc Function1)
请注意,Function1没有定义一个总函数,可能是PartialFunction的存在所暗示的。 Function1和PartialFunction之间的唯一区别是后者可以指定它不会处理的输入。
另一种思考方式是pf不是fn的子类型。
fn:String => INT
pf:String的子集=> INT
进一步编辑以回应评论: 不,我认为LSP根本不适用。对于要应用的LSP,S不得抛出新的异常。这里确实抛出了新的异常,因此LSP不会被违反,因为它不适用。