Laufzeit Polymorphismus Entscheidung

8

Ich habe irgendwo gelesen, dass der Laufzeitpolymorphismus ein Ergebnis der dynamischen Eingabe in Sprachen ist. Wenn wir den folgenden Code untersuchen, sehen wir ein klares Beispiel für den Laufzeitpolymorphismus.

%Vor%

Da es eine Superklassenreferenz gibt, kann der Compiler nicht entscheiden, welcher Typ sich referenzieren soll und ob er die Methode in der runtime bindet. Was ist aber mit der Verwendung der folgenden Klassendefinitionen?

Meine erste Frage ist für das Beispiel unten;

%Vor%

Es gibt nur eine Version der Methode do () in der Hierarchie. Wartet das System noch auf die Laufzeit, um die Methode zu binden, oder bindet es in der Kompilierzeit?

Meine zweite Frage ist für das Beispiel unten;

%Vor%

Es gibt jetzt eine Unterklasse (die niedrigste Vererbungskette). Wird es in der Laufzeit gebunden sein?

    
Mehmet Çağrı Köse 03.11.2015, 12:56
quelle

3 Antworten

4

Beim Kompilieren eines Aufrufs einer nicht-statischen Methode verwendet javac immer eine invokevirtual Anweisung, sodass zum Zeitpunkt der Kompilierung keine Optimierung durchgeführt wird.

Da jedoch die Devirtualisierung von Methodenaufrufen eine wichtige Optimierung ist (Speichern der Vtable-Suche, möglicherweise Inline-Methode), wird die Laufzeitumgebung (Hotspot usw.) versuchen , sie nach Möglichkeit durch Codeanalyse anzuwenden .

In Ihrem zweiten Beispiel (dritter Codeblock) könnte die Laufzeit erkennen, dass sie den virtuellen Aufruf von A.do durch einen Aufruf von B.do ersetzen kann, da ex ist eigentlich ein B (sollte in diesem Fall trivial sein, um die Laufzeit herauszufinden).

Für Ihr erstes Beispiel (zweiter Codeblock) gibt es eine weitere Optimierungstechnik. Die Laufzeit sieht zuerst die Klasse A . Alle Aufrufe von A.do werden jetzt als statische Aufrufe kompiliert, als ob keine abgeleitete Klasse existiert, die A.do überschreibt. Wenn eine solche Klasse später geladen wird, wird die Laufzeit diese positive Annahme zurücknehmen und stattdessen virtuelle Methodenaufrufe einführen.

    
wero 03.11.2015 13:19
quelle
3

Erstens ist der Begriff "dynamische Typisierung", wie er in Ihrer Frage verwendet wird, bestenfalls irreführend. Java ist nicht eine "dynamisch typisierte Programmiersprache". Es bietet bestimmte dynamische Typprüfungen, wie Typumwandlungen und den Operator instanceof . In Ihrem Codebeispiel ist jedoch keine dynamische Typüberprüfung beteiligt. Es ist alles statisch getippt.

Übrigens ist do in Java kein zulässiger Methodenname. Aber angenommen, A deklariert eine Methode doSomething und es gibt eine Unterklasse B , dann ist es für den Java-Compiler völlig irrelevant, ob B doSomething überschreibt oder nicht (es sei denn, die Zugriffsmodifizierer werden geändert).

Der Punkt ist, A und B sind verschiedene Klassen und können unabhängig voneinander (neu) kompiliert werden, und es gibt keine Garantie, dass B diese Methode zur Laufzeit immer noch (nicht) überschreibt. Die Spezifikation berücksichtigt jedoch, dass eine solche Änderung innerhalb des zulässigen Bereichs liegt, der die binäre Kompatibilität nicht beeinträchtigen sollte:

  

13.4.24. Überschreiben der Methode

     

Wenn eine Instanzmethode zu einer Unterklasse hinzugefügt wird und eine Methode in einer Oberklasse überschreibt, wird die Unterklassenmethode durch Methodenaufrufe in bereits vorhandenen Binärdateien gefunden, und diese Binärdateien sind nicht betroffen.

     

Wenn einer Klasse eine Klassenmethode hinzugefügt wird, wird diese Methode nicht gefunden, es sei denn, der qualifizierende Typ der Referenz ist der Unterklasse-Typ.

Beachten Sie den letzten Satz bezüglich "Klassenmethoden", aka static Methoden. Wenn A eine static Methode m deklariert und Sie sie über B.m aufrufen, kann der Aufruf in einer static -Methode enden, die von B deklariert wird, wenn B eine solche Methode deklariert zur Laufzeit, auch wenn die zur Kompilierzeit gesehene Version dies nicht tat. Selbst die früh gebundenen, nicht polymorphen Methoden werden schließlich zur Laufzeit aufgelöst und finden möglicherweise ein anderes Ziel als zur Kompilierungszeit gefunden. Der Unterschied zu spät gebundenen Methoden besteht darin, dass der Aufruf immer dann an die Methode gesendet wird, wenn die Methoden für die frühzeitige Bindung aufgelöst wurden. Dies hängt nicht von einer Eigenschaft ab, die zur Laufzeit geändert werden kann.

Bei den überschreibbaren Methoden wird die Methode nach dem Kompilierzeittyp der Referenz aufgelöst, für die sie aufgerufen wird, und dann kann es eine überschreibende Methode im tatsächlichen Laufzeittyp der Referenz geben . Dies ist der richtige Ort, um eine mögliche Folgefrage zu beantworten:

  

13.4.17. endgültige Methoden

     

Ändern einer Methode, die als final deklariert ist, wird nicht mehr deklariert final bricht die Kompatibilität mit bereits vorhandenen Binärdateien nicht.

Mit anderen Worten, wenn Sie eine Methode aufrufen, die final zur Kompilierzeit ist, wird der Compiler keinen Vorteil daraus ziehen, dass die Zielmethode final ist, da es möglich ist, dass die Methode nicht% ist. co_de% zur Laufzeit und diese Möglichkeit darf die Kompatibilität nicht unterbrechen.

Der einzige Methodenaufruf, der eine spezielle Behandlung erhält, ist der Aufruf von final Methoden. Da der Aufrufer einer private -Methode immer innerhalb derselben Klasse wie die Methodendeklaration selbst liegt, werden sie immer zusammen kompiliert und unterliegen keiner unabhängigen Evolution.

    
Holger 04.11.2015 12:30
quelle
0

Ob der Compiler zur Comile-Zeit bindet oder die JVM zur Laufzeit bindet, bleibt absichtlich undefiniert. Die Java-Spezifikation wird nicht die eine oder andere Art angeben - es wird nur angeben, dass zum Zeitpunkt der Ausführung des Codes die erwarteten Ergebnisse erreicht werden [Zitat benötigt].

Die frühe / späte Bindung ist eine Optimierung und daher optional.

    
OldCurmudgeon 03.11.2015 13:20
quelle