Scala:动态调度

时间:2017-05-29 12:26:10

标签: scala dynamic-dispatch

考虑这个Scala代码:

class X {
    def m(a:A) = a.f(this) + ", " + "m(a:A) in X"
}

class Y extends X {
    override def m(a:A) = a.f(this) + ", " + "m(a:A) in Y"
}

class Z extends Y

class A {
    def f(x:X):String = "f(x:X) in A"
    def f(y:Y):String = "f(y:Y) in A"
}

class B extends A {
    override def f(x:X):String = "f(x:X) in B"
    def f(z:Z):String = "f(z:Z) in B"
    def g(x:X):String = super.f(x) + ", " + "g(x:X) in B"
    def h(y:Y):String = B.g(y) + ", " + "h(y:Y) in B"
}

object B {
    def f(x:X) = "f(x:X) in ObjB"
    def g(y:Y) = f(y) + ", " + "g(y:Y) in ObjB"
    def g(z:Z) = f(z) + ", " + "g(z:Z) in ObjB"
}

class C extends B {
    override def f(y:Y):String = "f(y:Y) in C"
    def h(z:Z):String = super.h(z) + ", " + "h(z:Z) in C"
    def k(x:X):String = x.m(this) + ", " + "k(x:X) in C"
}

给出的问题是:给出运行以下程序的输出:

val z: Z = new Z; val x: X = new Y
val c: C = new C; val a: A = new B
println(a.f(x)); println(a.f(z))
println(c.g(z)); println(c.h(z))
println(c.k(x))

我似乎无法完全理解给出的输出。这就是我认为发生的事情:

println(a.f(x)) = "f(x:X) in B"
println(a.f(z)) = "f(z:Z) in B"
println(c.g(z)) = "f(y:Y) in A, g(x:X) in B"
println(c.h(z)) = "f(y:Y) in A, g(y:Y) in ObjB, h(y:Y) in B, h(z:Z) in C"
println(c.k(x)) = "f(y:Y) in C, m(a:A) in Y, k(x:X) in C"

然而,当我实际将代码放入REPL时,我得到了不同的结果:

println(a.f(x)) = "f(x:X) in B"
println(a.f(z)) = "f(y:Y) in A"
println(c.g(z)) = "f(x:X) in A, g(x:X) in B"
println(c.h(z)) = "f(x:X) in ObjB, g(y:Y) in ObjB, h(y:Y) in B, h(z:Z) in C"
println(c.k(x)) = "f(y:Y) in C, m(a:A) in Y, k(x:X) in C"

这是为什么?我知道这与如何选择重载方法有关,但我找不到一个精确指定如何确定选择哪些方法的单一资源,以及何时。如果有人能解释确切的过程,我将不胜感激。

1 个答案:

答案 0 :(得分:3)

我不认为这与线性化有关,因为示例中没有多重继承。此示例与方法overridingoverloading一起发送。

首先我们需要分开"实际类型"和"声明的类型"。适用于

中的检验
val x: X = new Y

变量x声明类型为X,实际类型为Y。您无法更改值的实际类型,但如果它们是超类型,您可以将某些实际类型的值分配给不同声明类型的变量。

调用分派有一个简单(和简化)规则:对于首先调度的方法重载,即当一个对象作为参数传递时,其声明的类型用于选择方法;对于方法重写,即在对象上调用方法时,其实际类型用于选择方法。

附加规则this始终声明声明它的类的类型。

此外,您还应该学习如何调试应用程序。调试很容易显示调用哪些方法。

<强>类型

val x: X = new Y
val z: Z = new Z; 
val a: A = new B
val c: C = new C; 
  • x已声明类型为X且实际类型为Y
  • z包含Z
  • 的已声明和实际类型
  • a已声明类型为A且实际类型为B
  • c包含C
  • 的已声明和实际类型

示例#1是微不足道的,所以我将省略它。

<强>#2

println(a.f(z)) = "f(y:Y) in A"

这里我们首先需要解决重载,即在类型A方法中查找接受最接近Z的类型。它是

def f(y:Y):String = "f(y:Y) in A"

现在我们需要调度重写,即检查B是否覆盖此方法且答案为否,B中的方法没有覆盖,因此{{1}中的方法生成输出}}

A&#39;

的原因
B
未选择

的是def f(z:Z):String = "f(z:Z) in B" 声明 类型a,因此A更具体方法在此上下文中不可见。

<强>#3

B

这里我们首先需要解决重载,即在类型println(c.g(z)) = "f(x:X) in A, g(x:X) in B" 方法中查找接受最接近C的类型。这是Z&#39>

B

我们可以看到def g(x:X):String = super.f(x) + ", " + "g(x:X) in B" 中没有覆盖此方法,因此它会产生输出。从C

开始,super.f(x)表示A.f(x)

<强>#4

B extends A

这里我们首先需要解决重载,即在类型println(c.h(z)) = "f(x:X) in ObjB, g(y:Y) in ObjB, h(y:Y) in B, h(z:Z) in C" 方法中查找接受最接近C的类型。这是Z&#39>

C

这给了我们答案的最后部分。现在def h(z:Z):String = super.h(z) + ", " + "h(z:Z) in C" 表示super.h(z)&#39;

B

因为def h(y:Y):String = B.g(y) + ", " + "h(y:Y) in B" 中没有h(Z)所以我们需要搜索接受基本类型B的方法。这使我们在答案的最后部分排在第二位。现在ZB.g(y)

的电话
object B

这是因为在def g(y:Y) = f(y) + ", " + "g(y:Y) in ObjB" 声明的类型为h(y:Y)而非原始(和实际)Y的上下文中。显然,此处Zf(y)

的电话
object B

<强>#5

def f(x:X) = "f(x:X) in ObjB"

我们首先需要解决重载,即在类型println(c.k(x)) = "f(y:Y) in C, m(a:A) in Y, k(x:X) in C" 方法中查找接受最接近C的类型。这是X&#39>

C

这给了我们答案的最后部分。现在解决def k(x:X):String = x.m(this) + ", " + "k(x:X) in C" :我们需要找到接受x.m(this)最接近类型的X方法m

C

但这是第一次 覆盖 的步骤。此处def m(a:A) = a.f(this) + ", " + "m(a:A) in X" 的实际类型为x,因此调用将发送到{ {1}}&#39; S

Y

现在我们需要解决Yoverride def m(a:A) = a.f(this) + ", " + "m(a:A) in Y" 此处声明的类型为a.f(this),所以

this

Y中的匹配,但def f(y:Y):String = "f(y:Y) in A" 覆盖了此方法,所以

A

将被选中。