Gestern hatte ich ein zweistündiges technisches Telefoninterview (das ich bestanden habe, woohoo!), Aber ich habe die folgende Frage bezüglich der dynamischen Bindung in Java vollständig beantwortet. Und es ist doppelt rätselhaft, weil ich Studenten vor einigen Jahren als TA dieses Konzept beigebracht habe. Die Aussicht, dass ich ihnen Fehlinformationen gegeben habe, ist also etwas beunruhigend ...
Hier ist das Problem, das mir gegeben wurde:
/* What is the output of the following program? */
public class Test {
public boolean equals( Test other ) {
System.out.println( "Inside of Test.equals" );
return false;
}
public static void main( String [] args ) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();
int count = 0;
System.out.println( count++ );// prints 0
t1.equals( t2 ) ;
System.out.println( count++ );// prints 1
t1.equals( t3 );
System.out.println( count++ );// prints 2
t3.equals( o1 );
System.out.println( count++ );// prints 3
t3.equals(t3);
System.out.println( count++ );// prints 4
t3.equals(t2);
}
}
Ich habe behauptet, dass die Ausgabe zwei separate Druckanweisungen innerhalb der überschriebenen equals()
Methode sein sollte: at t1.equals(t3)
und t3.equals(t3)
. Der letztere Fall ist offensichtlich genug, und im ersteren Fall wird er, obwohl er t1
eine Referenz vom Typ Object hat, als Typ Test instanziiert, sodass die dynamische Bindung die überschriebene Form der Methode aufrufen sollte.
Scheinbar nicht. Mein Interviewer ermutigte mich, das Programm selbst auszuführen, und siehe da, es gab nur eine einzige Ausgabe der überschriebenen Methode: an der Leitung t3.equals(t3)
.
Meine Frage ist dann, warum? Wie bereits erwähnt, sollte die dynamische Bindung dafür sorgen, dass die spezifischste Version der Methode basierend auf dem instanziierten Typ der Referenz aufgerufen wird, obwohl t1
es sich um eine Referenz vom Typ Object handelt (statische Bindung würde also die equals()
Methode von Object aufrufen ) . Was vermisse ich?
quelle
Antworten:
Java verwendet statische Bindung für überladene Methoden und dynamische Bindung für überschriebene. In Ihrem Beispiel ist die Methode equals überladen (hat einen anderen Parametertyp als Object.equals ()), sodass die aufgerufene Methode zur Kompilierungszeit an den Referenztyp gebunden ist .
Einige Diskussionen hier
Die Tatsache, dass es sich um die Equals-Methode handelt, ist nicht wirklich relevant, abgesehen davon, dass es ein häufiger Fehler ist, sie zu überladen, anstatt sie zu überschreiben, was Ihnen bereits aufgrund Ihrer Antwort auf das Problem im Interview bekannt ist.
Edit: Eine gute Beschreibung auch hier . Dieses Beispiel zeigt ein ähnliches Problem im Zusammenhang mit dem Parametertyp, das jedoch durch dasselbe Problem verursacht wird.
Ich glaube, wenn die Bindung tatsächlich dynamisch wäre, würde jeder Fall, in dem der Aufrufer und der Parameter eine Instanz von Test wären, dazu führen, dass die überschriebene Methode aufgerufen wird. T3.equals (o1) wäre also der einzige Fall, der nicht gedruckt würde.
quelle
Die
equals
Methode vonTest
überschreibt dieequals
Methode von nichtjava.lang.Object
. Schauen Sie sich den Parametertyp an! DieTest
Klasse wirdequals
mit einer Methode überladen , die a akzeptiertTest
.Wenn die
equals
Methode überschrieben werden soll, sollte die Annotation @Override verwendet werden. Dies würde einen Kompilierungsfehler verursachen, der auf diesen häufigen Fehler hinweist.quelle
Interessanterweise würden in Groovy-Code (der zu einer Klassendatei kompiliert werden könnte) alle Aufrufe bis auf einen die print-Anweisung ausführen. (Derjenige, der einen Test mit einem Objekt vergleicht, ruft die Test.equals (Test) -Funktion eindeutig nicht auf.) Dies liegt daran, dass groovy DOES eine vollständig dynamische Typisierung ausführt. Dies ist besonders interessant, da es keine Variablen gibt, die explizit dynamisch typisiert werden. Ich habe an einigen Stellen gelesen, dass dies als schädlich angesehen wird, da Programmierer erwarten, dass Groovy das Java-Ding macht.
quelle
Java unterstützt keine Ko-Varianz bei Parametern, sondern nur bei Rückgabetypen.
Mit anderen Worten, während Ihr Rückgabetyp in einer überschreibenden Methode möglicherweise ein Untertyp dessen ist, was er in der überschriebenen Methode war, gilt dies nicht für Parameter.
Wenn Ihr Parameter für Gleichheit in Objekt Objekt ist, ist das Einfügen einer Gleichheit mit etwas anderem in einer Unterklasse eine überladene und keine überschriebene Methode. Daher ist die einzige Situation, in der diese Methode aufgerufen wird, wenn der statische Typ des Parameters Test ist, wie im Fall von T3.
Viel Glück beim Bewerbungsgespräch! Ich würde gerne in einem Unternehmen interviewt werden, das diese Art von Fragen anstelle der üblichen Fragen zu Algo / Datenstrukturen stellt, die ich meinen Schülern beibringe.
quelle
Ich denke, der Schlüssel liegt in der Tatsache, dass die Methode equals () nicht dem Standard entspricht: Sie nimmt ein anderes Testobjekt auf, kein Objektobjekt, und überschreibt daher die Methode equals () nicht. Dies bedeutet, dass Sie es tatsächlich nur überladen haben, um etwas Besonderes zu tun, wenn es ein Testobjekt erhält, während es Objektobjektaufrufe Object.equals (Object o) erhält. Wenn Sie diesen Code durch eine IDE durchsuchen, sollten Sie zwei equals () -Methoden für den Test anzeigen.
quelle
Die Methode wird überladen anstatt überschrieben. Gleiche nehmen immer ein Objekt als Parameter.
Übrigens haben Sie einen Gegenstand dazu in Blochs effektivem Java (den Sie besitzen sollten).
quelle
Einige Hinweise in Dynamic Binding (DD) und Static Binding̣̣̣ (SB) nach einer Weile der Suche:
1.Timing ausführen : (Ref.1)
2. Verwendet für :
Referenz:
quelle
Wenn eine andere Methode hinzugefügt wird, die überschreibt, anstatt zu überladen, wird der dynamische Bindungsaufruf zur Laufzeit erläutert.
/ * Was ist die Ausgabe des folgenden Programms? * /
quelle
Ich habe einen interessanten Artikel über dynamische und statische Bindung gefunden. Es wird mit einem Code zur Simulation der dynamischen Bindung geliefert. Es machte meinen Code lesbarer.
https://sites.google.com/site/jeffhartkopf/covariance
quelle
Die Antwort auf die Frage "Warum?" So wird die Java-Sprache definiert.
Um den Wikipedia-Artikel über Kovarianz und Kontravarianz zu zitieren :
Andere Sprachen sind anders.
quelle
Es ist sehr klar, dass es hier kein Konzept gibt, etwas zu überschreiben. Es ist eine Methodenüberladung. Die
Object()
Methode der Objektklasse verwendet einen Referenzparameter vom Typ Objekt und dieseequal()
Methode verwendet einen Referenzparameter vom Typ Test.quelle
Ich werde versuchen, dies anhand von zwei Beispielen zu erklären, bei denen es sich um die erweiterten Versionen einiger Beispiele handelt, die mir online begegnet sind.
Hier für Zeilen mit Zählwerten 0, 1, 2 und 3; Wir haben die Referenz von Object für o1 und t1 auf der
equals()
Methode. Daher wird beim Kompilieren dieequals()
Methode aus der Object.class- Datei begrenzt.Doch obwohl Bezug von t1 ist Object , hat es intialization von Test - Klasse .
Object t1 = new Test();
.Daher ruft es zur Laufzeit das auf,
public boolean equals(Object other)
was ein ist.
Für Zählwerte als 4 und 6 ist es wieder einfach, dass t3, das Referenz und Initialisierung von Test hat, eine
equals()
Methode mit Parameter als Objektreferenzen aufruft und eine istOK!
quelle