Warum kann die Überladung nicht zur Laufzeit implementiert werden?

8

Siehe das folgende Beispiel:

%Vor%

Warum unterstützen Überladungsmethoden nicht das Upcasting?

Wenn das Überladen zur Laufzeit implementiert wurde, bietet es viel mehr Flexibilität. Zum Beispiel wäre das Besuchermuster einfacher. Gibt es einen technischen Grund, der Java daran hindert, dies zu tun?

    
Don Li 23.05.2012, 04:20
quelle

7 Antworten

6

Die Überladungsauflösung beinhaltet einige nicht-triviale Regeln, um zu bestimmen, welche Überladung am besten passt, und es wäre schwierig, diese zur Laufzeit effizient auszuführen. Im Gegensatz dazu ist das Aufheben der Auflösung einfacher - im harten Fall müssen Sie nur die Funktion foo für die Klasse des Objekts und im einfachen Fall nachschlagen (z. B. wenn es nur eine Implementierung oder nur eine Implementierung in diesem Codepfad gibt ), können Sie die virtuelle Methode in einen statisch kompilierten, nicht-virtuellen, nicht-dynamisch-dispatchenden Aufruf umwandeln (wenn Sie es basierend auf dem Codepfad tun, müssen Sie eine schnelle Überprüfung durchführen, um zu überprüfen, ob das Objekt ist eigentlich die, die du erwartest).

Wie sich herausstellt, ist es eine gute Sache, dass Java 1.4 und niedriger keine Override-Auflösung für die Laufzeit hatte, weil Generika dadurch schwerer nachzurüsten wären. Generics spielen eine Rolle bei der Override-Auflösung, aber diese Information wäre zur Laufzeit aufgrund von Löschung nicht verfügbar.

    
yshavit 23.05.2012 04:33
quelle
4

Es gibt keinen theoretischen Grund, warum dies nicht möglich ist. Das Common Lisp Object System unterstützt diese Art von Konstruktion - genannt multiple dispatch - obwohl dies in einem etwas anderen Paradigma geschieht (Methoden, anstatt an Objekte gebunden zu sein, sind Instanzen von Generika (oder generische Funktionen), die zur Laufzeit virtuelle Werte für die Werte mehrerer Parameter bereitstellen können). Ich glaube, es gab auch Erweiterungen für Java, um es zu aktivieren (Multi-Java kommt mir in den Sinn, obwohl das möglicherweise mehrfache Vererbung und nicht mehrfaches Versenden gewesen ist).

Es kann jedoch Gründe für die Java-Sprache geben, warum es nicht möglich ist, außer dass die Sprachdesigner denken, dass es nicht gemacht werden sollte, dass ich andere über Vernunft belästige. Es führt jedoch Komplikationen für die Vererbung ein. Überlegen Sie:

%Vor%

Welche invoke wird aufgerufen? %Code%? %Code%? Was ist Baz ? Es ist möglich, eine deterministische Semantik zu entwickeln, aber sie wird zumindest in einigen Fällen Verwirrung und Überraschung mit sich bringen. Angesichts der Tatsache, dass Java eine einfache Sprache sein will, glaube ich nicht, dass Merkmale, die eine solche Verwirrung hervorrufen, als mit ihren Zielen übereinstimmend angesehen werden.

    
Michael Ekstrand 24.05.2012 15:14
quelle
3
  

Gibt es einen technischen Grund, der Java daran hindert, dies zu tun?

Code-Korrektheit : Ihr aktuelles Beispiel bietet zwei Implementierungen von I und zwei entsprechende Methoden f. Nichts verhindert jedoch die Existenz anderer Klassen, die I implementieren - das Verschieben der Auflösung auf Laufzeit würde auch Kompilierungsfehler zu möglicherweise verborgenen Laufzeitfehlern ersetzen.

Performance : Wie andere bereits erwähnt haben, ist das Überladen von Methoden mit ziemlich komplexen Regeln verbunden. Dies ist sicherlich schneller als bei jedem Methodenaufruf zur Laufzeit.

Abwärtskompatibilität : Derzeit überladene Methoden werden mit dem Kompilierzeittyp der übergebenen Argumente und nicht mit ihrem Laufzeittyp aufgelöst. Wenn Sie das Verhalten so ändern, dass Laufzeitinformationen verwendet werden, werden viele vorhandene Anwendungen beschädigt.

>

So umgehen Sie das Problem

Benutze das Besuchermuster, ich verstehe nicht, wie jemand denkt, dass es schwer ist.

%Vor%     
josefx 26.05.2012 21:32
quelle
3

Ich glaube nicht, dass irgendjemand außer den Designern der Sprache diese Frage beantworten könnte. Ich bin kein Experte auf diesem Gebiet, aber ich werde nur meine Meinung geben.

Durch Lesen der JLS 15.12 über Methode Aufrufausdrücke, es ist ziemlich klar, dass die Auswahl der richtigen Methode zur Ausführung ein bereits komplizierter Kompilierzeitprozess ist; vor allem nach der Einführung von Generika.

Stellen Sie sich nun vor, dass Sie all dies zur Laufzeit verschieben, nur um die einzelnen Funktionen von Mutemethoden zu unterstützen. Für mich hört sich das wie ein kleines Feature an, das der Sprache zu viel Komplexität hinzufügt, und wahrscheinlich ein Feature mit bestimmten Auswirkungen auf die Leistung, da alle diese Entscheidungen zur Laufzeit und nicht nur einmal getroffen werden müssen. wie es heute ist, zur Kompilierzeit.

Zu all diesen können wir hinzufügen, dass Typ löschen es wäre unmöglich, den tatsächlichen Typ bestimmter generischer Typen zu bestimmen. Es scheint mir, dass es nicht im besten Interesse von Java ist, die Sicherheit der statischen Typprüfung aufzugeben.

Auf jeden Fall gibt es gültige Alternativen, um mit dem Problem des Mehrfachversands umzugehen, und vielleicht rechtfertigen diese Alternativen ziemlich genau, warum es in der Sprache nicht implementiert wurde. Sie können also das klassische Besuchermuster verwenden oder eine bestimmte Menge an Reflektion verwenden.

Es gibt ein veraltetes MultiJava-Projekt , das mehrere Dispatch-Unterstützung in Java implementiert hat und es gibt noch ein paar andere Projekte, die Reflektionen verwenden unterstützen Multimethoden in Java: Java Multimethods , Java Multimethods Framework . Vielleicht gibt es noch mehr.

Sie könnten auch eine alternative Java-basierte Sprache in Erwägung ziehen, die Multimethoden unterstützt, wie Clojure oder Groovy .

Da C # eine Sprache ist, die Java in seiner allgemeinen Phillosopie sehr ähnlich ist, könnte es auch interessant sein, mehr darüber zu erfahren, wie es funktioniert. unterstützt Multimethods und untersucht, welche Auswirkungen das Angebot eines ähnlichen Features in Java haben würde. Wenn Sie glauben, dass es eine Funktion ist, die Sie in Java haben sollten, können Sie sogar ein JEP einreichen, das für zukünftige Versionen berücksichtigt werden kann der Java-Sprache.

    
Edwin Dalorzo 23.05.2012 06:44
quelle
2

Nicht die Antwort auf Java. Diese Funktionalität existiert jedoch in C # 4:

%Vor%

Ausgabe:

%Vor%

Wenn Sie C # 3 und darunter verwenden, müssen Sie Reflektion verwenden, ich habe einen Post darüber in meinem Blog Mehrere Dispatch in C # : Ссылка

Wenn Sie in Java mehrfach versenden möchten, können Sie die Reflektionsroute wählen.

Hier ist eine andere Lösung für Java: Ссылка

    
Michael Buen 23.05.2012 04:47
quelle
2

Schätze, du musst dich nur mit Reflektion begnügen:

%Vor%

Ausgabe:

%Vor%     
Michael Buen 26.05.2012 00:04
quelle
1

Ihre Methode sagt, dass sie A oder B akzeptiert, die abgeleitete Klassen von I sind, sie können mehr Details enthalten als I

%Vor%

Wenn Sie versuchen, eine Superklasse von A in Ihrer Case-Schnittstelle I zu senden, wünscht der Compiler eine Bestätigung, dass Sie tatsächlich A senden, während Details in A möglicherweise nicht in I verfügbar sind. auch nur während der Laufzeit I bezieht sich tatsächlich auf eine Instanz von A keine solchen Informationen zur Kompilierzeit zur Verfügung, so müssen Sie den Compiler explizit sagen, dass I ist tatsächlich A oder B und Sie tun eine Besetzung, um das zu sagen.

    
mprabhat 23.05.2012 04:32
quelle