3

考虑这个 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"

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

4

1 回答 1

4

我认为这与线性化没有任何关系,因为示例中没有多重继承。这个例子是关于方法覆盖重载的调度。

首先我们需要区分“实际类型”和“声明类型”。例如在

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所以输出是由 中的方法生成的A

的原因B

def f(z:Z):String = "f(z:Z) in B"

未选择的是a声明类型的A,因此B在此上下文中看不到更具体的方法。

#3

println(c.g(z)) = "f(x:X) in A, g(x:X) in B"

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

def g(x:X):String = super.f(x) + ", " + "g(x:X) in B"

我们可以看到这个方法没有被覆盖,C所以它会产生输出。super.f(x)意味着A.f(x)因为B extends A

#4

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。它C

def h(z:Z):String = super.h(z) + ", " + "h(z:Z) in C"

这给了我们答案的最后一部分。现在的super.h(z)意思B

def h(y:Y):String = B.g(y) + ", " + "h(y:Y) in B"

因为没有h(Z)B所以我们需要搜索接受基本类型的方法Z。这给了我们答案的最后一部分。现在B.g(y)object B's的调用

def g(y:Y) = f(y) + ", " + "g(y:Y) in ObjB"

之所以如此,是因为在h(y:Y)声明类型的上下文中,Y而不是原始(和实际)Z。显然这里f(y)object B's的调用

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

#5

println(c.k(x)) = "f(y:Y) in C, m(a:A) in Y, k(x:X) in C"

同样,我们首先需要解决重载问题,即在C接受最接近的类型的类型方法中查找X。它C

def k(x:X):String = x.m(this) + ", " + "k(x:X) in C"

这给了我们答案的最后一部分。现在解决x.m(this):我们需要在接受最接近的类型的X方法中找到:mC

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

然而,这是第一次 覆盖步骤。x这里的实际类型是Y这样的调用将Y

override 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但这个方法再次被覆盖C所以

override def f(y:Y):String = "f(y:Y) in C"

将被选中。

于 2017-05-29T14:00:25.037 回答