2017-05-29 5 views
0

Betrachten Sie diese Scala Code:Scala: Dynamische Dispatch-

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" 
} 

Die Frage gegeben ist: Geben Sie die Ausgabe der Ausführung des folgenden Programms:

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)) 

ich nicht ganz scheinen, um die Ausgabe zu verstehen gegeben. Hier ist, was ich denke, geschieht:

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" 

Doch wenn ich tatsächlich den Code in REPL setzen, ich ein anderes Ergebnis:

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" 

Warum ist das? Ich weiß, dass es etwas damit zu tun hat, wie überladene Methoden ausgewählt werden, aber ich konnte keine einzige Ressource finden, die genau angibt, wie man bestimmt, welche Methoden wann ausgewählt werden. Wenn jemand den genauen Ablauf erklären könnte, wäre ich dankbar.

+0

Also ist das diff nur in 'c.h'? Es gibt keinen Anruf zu "A" irgendwo in "h". – Reactormonk

+0

@Reactorymonk Nein, die Unterschiede sind in 'a.f (z)', 'c.g (z)' und in 'c.h (z)'. – Jolyon

Antwort

1

Ich glaube nicht, dass dies etwas mit Linearisierung zu tun hat, da es im Beispiel keine Mehrfachvererbung gibt. In diesem Beispiel geht es um den Versand mit den Methoden overriding und overloading.

Zuerst müssen wir "tatsächlichen Typ" und "deklarierten Typ" trennen. Für examlpe in

val x: X = new Y 

für variable x declare Typ ist X und tatsächliche Typ ist Y. Sie können den tatsächlichen Typ eines Werts nicht ändern, aber Sie können Variablen eines anderen Typs Variablen zuweisen, wenn sie Super-Typen sind.

Es gibt eine einfache (und etwas vereinfachte) Regel für die Anrufverteilung: Beim Methodenüberladen, das zuerst ausgelöst wird, d. H. Wenn ein Objekt als Parameter übergeben wird, wird der deklarierte Typ zur Auswahl der Methode verwendet; zum Überschreiben der Methode, d.h. wenn eine Methode für ein Objekt aufgerufen wird, wird ihr tatsächlicher Typ verwendet, um die Methode auszuwählen.

Die zusätzliche Regel this hat immer den Typ der Klasse deklariert, in der er deklariert ist.

Auch Sie sollten lernen, wie Sie Ihre Anwendungen debuggen. Das Debugging zeigt Ihnen leicht, welche Methoden aufgerufen werden.

Typen

In

val x: X = new Y 
val z: Z = new Z; 
val a: A = new B 
val c: C = new C; 
  • x hat Y Art von X und tatsächlichen Typ deklariert
  • z hat beide erklärt und tatsächlichen Arten von Z
  • a hat Art von A und tatsächlichen Typ deklariert B
  • c beide und tatsächliche Arten von C

Beispiel # 1 erklärt hat, ist trivial, also werde ich es weglassen.

# 2

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

Hier müssen wir zuerst lösen das heißt in Art A Methode finden Überlastung, die am nächsten zu Z akzeptiert eingeben. Es ist

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

Jetzt müssen wir überwiegendes heißt Versand überprüfen, ob B überschreibt diese Methode und die Antwort ist nein, da für diese Methode in B keine Überschreibung ist so Ausgabe durch das Verfahren in A erzeugt wird.

Der Grund, warum B 's

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

nicht ausgewählt ist, ist, dass aerklärt Art von A hat und somit B' s spezifischere Methode ist in diesem Zusammenhang nicht sichtbar.

# 3

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

Hier müssen wir zuerst lösen das heißt in Art C Methode finden Überlastung, die am nächsten zu Z akzeptiert eingeben. Es ist B ‚s

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

Wir können sehen, dass diese Methode nicht in C außer Kraft gesetzt wird, so wird es Ausgabe. super.f(x) bedeutet A.f(x) seit 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" 

Hier haben wir zunächst in Art C Methode finden Überlastung beheben müssen, das heißt, der Typ am nächsten Z akzeptiert.Es ist C ‚s

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

Diese geben uns im letzten Teil der Antwort. Jetzt super.h(z) bedeutet B ‚s

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

, weil es keine h(Z) im B ist so müssen wir nach Methoden suchen, die Basistypen von Z akzeptieren. Dies gibt uns den zweiten Teil der Antwort an zweiter Stelle. Jetzt B.g(y) ist ein Aufruf von object B ‚s

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

Dies ist so, weil im Rahmen der h(y:Y) deklarierten Typ ist Y eher als Original (und tatsächlich) Z. Offensichtlich hier f(y) ist ein Aufruf von 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" 

Wieder müssen wir zuerst eine Überlastung lösen heißt in Art finden C Methode, die am nächsten Typ zu X akzeptiert. Es ist C ‚s

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

Diese uns im letzten Teil der Antwort gibt. Jetzt lösen x.m(this): wir in X Methode m finden müssen, die C Typen am nächsten akzeptiert:

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

Dies ist jedoch das erste Mal, wo überwiegende Schritte in Actual Art von x hier sind Y so die. Anruf wird

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

Jetzt Y ‚s sein, die wir brauchen a.f(this) zu lösen. this hat hier einen deklarierten Typ von Y so

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

ein Spiel in A ist aber auch dieses Verfahren in C außer Kraft gesetzt wird, so

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

ausgewählt.

+0

Vielen Dank für eine sehr fantastische und detaillierte Antwort. Lassen Sie mich den Kontext klären: Dies ist eine Frage in der Vergangenheit, und daher kann ich den Code nicht debuggen. Dies verdeutlicht jedoch die Konzepte sehr gut. Wenn beispielsweise g (x: X) ausgewählt wurde (nur ein allgemeines Beispiel), dann wird jeder Aufruf von der Funktion g annehmen, dass x den Typ X hat, selbst wenn es den Typ Y hat? Wenn also g (x: X) f (x) aufruft, wird es an f (x: X) angepasst, nicht an f (y: Y)? – Jolyon

+0

@Jolyon, ja jeder Aufruf innerhalb 'g', der' x' als Argument nimmt, wird angenommen, dass der Typ von 'x'' X' ist, weil dies die einzige sichere Sache für den Compiler ist.Wenn Sie jedoch eine Methode für 'x' ** _ und _ ** aufrufen, die in 'Y' überschrieben wird, wird die Methode'Y' aufgerufen (wenn sie nur in' Y' deklariert ist, erhalten Sie entweder einen Kompilierungsfehler oder weniger spezifische Methode in 'X' wird aufgerufen). P.S. Noch eine Sache, wenn Sie diesen Code schreiben können (und Sie sicher in der Frage getan haben), können Sie es selbst debuggen :) – SergGr

+0

Schauen Sie nur auf Ihre Antwort zurück, und ich glaube, Sie irren sich über den letzten Teil der letzten Frage 'println (ck (x))'. Sie setzen es als 'println (ck (x)) =" f (y: Y) in A, m (a: A) in Y, k (x: X) in C "', während es 'println sein sollte (ck (x)) = "f (y: Y) in C, m (a: A) in Y, k (x: X) in C" ', obwohl es in' override def m (a: A) = af (this) + "," + "m (a: A) in Y" ', der tatsächliche Typ von' a' in diesem Zusammenhang ist 'C', und da es eine überschreibende Methode' f (y: Y) 'in' C', dann 'f (y: Y)' heißt 'C', nicht' A'. – Jolyon