class X
class Y extends X
class Z extends Y
class M {
def f(x: X): String = "f with X at M"
def f(x: Y): String = "f with Y at M"
}
class N extends M {
override def f(x: Y): String = "f with Y at N"
def f(x: Z): String = "f with Z at N"
}
val z: Z = new Z
val y: Y = z
val x: X = y
val m: M = new N
println(m.f(x))
// m dynamically matches as type N and sees x as type X thus goes into class M where it calls "f with X at M"
println(m.f(y))
// m dynamically matches as type N and sees y as type Y where it calls "f with Y at N"
println(m.f(z))
// m dynamically matches as type N and sees z as type Z where it calls "f with Z at N"
考虑一下这段代码,我不能理解最终调用println(mf(z))的行为与我在评论中写的不一样 - 是否有很好的资源来理解Scala中的重载是如何工作的?
感谢!
答案 0 :(得分:2)
首先,Scala中的重载与Java中的重载相同。
其次,它是关于静态和动态绑定。让我们找出编译器看到的内容。你有m: M
个对象。类M
具有f(X)
和f(Y)
方法。当您调用m.f(z)
编译器时,应调用方法f(Y)
,因为Z
是Y
的子类。这是非常重要的一点:编译器不知道m
对象的真正类,这就是为什么它对方法N.f(Z)
一无所知。它被称为静态绑定:编译器解析方法的签名。稍后,在运行时,会发生动态绑定。 JVM知道m
的真实类,并调用f(Y)
中重载的Z
。
希望我的解释显然足以理解。
答案 1 :(得分:0)
class x
class Y extends X
class Z extends Y
class M {
def f(x: X): String = "f with X at M"
def f(x: Y): String = "f with Y at M"
}
class N extends M {
override def f(x: Y): String = "f with Y at N"
def f(x: Z): String = "f with Z at N"
}
val z: Z = new Z
val y: Y = z
val x: X = y
val m: M = new N
println(m.f(x))
// m dynamically matches as type N and sees x as type X thus goes into class M where it calls "f with X at M"
println(m.f(y))
// m dynamically matches as type N and sees y as type Y where it calls "f with Y at N"
println(m.f(z))
// m dynamically matches as type N and sees z as type Z where it calls "f with Z at N"
因为函数将在N上重载,所以N取决于m.f(y)。最后它与x和y有关,这是z函数将调用的原因
答案 2 :(得分:0)
当你这样做时
val m: M = new N
这意味着m能够完成M级所能做的所有事情。 M有两种方法 - 第一种可以采用X,其他采用Y.
因此当你这样做时
m.f(z)
运行时将搜索一个可以接受z(类型为Z)的方法。由于两个原因,N中的方法不是候选者
M
N
没有覆盖 M
的任何可以接受Z
类型参数的方法。您做在N
中有一个方法可以接受Z
,但这不是候选人,因为它没有覆盖{{1}的任何内容} M
中f
的最佳匹配可以接受M
这是因为Y
ISA Z
如果
,您可以获得上次评论所说的内容Y
中定义一个方法,该方法接受M
类型的参数,然后在Z
N
我认为关于SO的现有问题已经阐明了这一点。