我定义了一个特征:
trait A {
def hello(name:Any):Any
}
然后定义一个类X来实现它:
class X extends A {
def hello(name:Any): Any = {}
}
它汇编了。然后我更改子类中的返回类型:
class X extends A {
def hello(name:Any): String = "hello"
}
它也编译了。然后更改参数类型:
class X extends A {
def hello(name:String): Any = {}
}
这次无法编译,错误是:
error: class X needs to be abstract, since method hello in trait A of type (name: Any)
Any is not defined
(Note that Any does not match String: class String in package lang is a subclass
of class Any in package scala, but method parameter types must match exactly.)
参数似乎应该完全匹配,但返回类型可以是子类中的子类型吗?
更新:@ Mik378,感谢您的回答,但为什么以下示例无效?我认为它并没有打破Liskov:
trait A {
def hello(name:String):Any
}
class X extends A {
def hello(name:Any): Any = {}
}
答案 0 :(得分:6)
与Java完全一样,要保持Liskov Substitution principle,您无法覆盖具有更精细参数的方法。
确实,如果您的代码处理A
类型,并引用了X
类型,该怎么办?
根据{{1}},您可以传递所需的A
类型,但Any
仅允许B
。
因此=> BOOM
逻辑上,使用相同的推理,允许使用更精细的返回类型,因为任何处理String
类的代码都会覆盖任何情况。
您可能需要检查这些部分: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_method_return_type
和
UPDATE ----------------
A
这将是一个完美的超载,而不是压倒一切。
答案 1 :(得分:2)
在Scala中,可以使用相同名称但参数不同的方法:
class X extends A {
def hello(name:String) = "String"
def hello(name:Any) = "Any"
}
这称为方法重载,并镜像Java的语义(虽然我的例子很不寻常 - 通常重载的方法会做大致相同的事情,但使用不同的参数组合)。
您的代码无法编译,因为参数类型需要完全匹配才能覆盖才能工作。否则,它会将您的方法解释为具有不同参数类型的新方法。
但是,Scala或Java中没有允许重载返回类型的工具 - 重载仅取决于名称和参数类型。对于返回类型重载,除了最简单的情况之外,不可能确定要使用哪个重载变体:
class X extends A {
def hello: Any = "World"
def hello: String = "Hello"
def doSomething = {
println(hello.toString) // which hello do we call???
}
}
这就是为什么你的第一个例子没有问题编译的原因 - 你实现的方法没有歧义。
注意JVM学习者 - 从技术上讲,JVM确实区分具有不同返回类型的方法。但是,Java和Scala小心地仅使用此工具作为优化,并且它没有反映在Java或Scala的语义中。
答案 2 :(得分:1)
这不是我的首要问题,但基本上X.hello
符合A.hello
的要求,您需要输入X.hello
作为{{1}的超类输入(协方差)和A.hello
的输出是X.hello
输出的子类(逆变)。
想想这是以下
的具体情况A.hello
问题是"我可以将class A
class A' extends A
class B
class B' extends B
f :: A' -> B
g :: A -> B'
替换为表达式f
中的g
并在相同情况下仍然进行类型检查吗?
在该表达式中,y=f(x)
的类型为y
,x的类型为B
在A'
我们知道y=f(x)
的类型为y
,而x的类型为B
A'
仍然没问题,因为g(x)
的类型为x
(因此类型为A'
)
A
仍然没问题,因为y=g(x)
的类型为g(x)
(因此类型为B'
)
实际上,最简单的方法是B
是y
的子类型(即至少实现B
),这意味着您可以使用B
作为y
类型的值。你必须在另一件事上做一些心理体操。
(我只记得它是输入的一个方向,输出上的另一个方向,然后尝试一下......如果你想一想就会变得很明显。)