Die offensichtliche unbeantwortete Frage von C ++ nach Java ist, warum Java keine Überladung von Operatoren beinhaltete.
Ist nicht Complex a, b, c; a = b + c;
viel einfacher als Complex a, b, c; a = b.add(c);
?
Gibt es einen bekannten Grund dafür, gültige Argumente dafür , dass eine Überladung des Bedieners nicht zulässig ist ? Ist der Grund willkürlich oder zeitlos?
java
operator-overloading
Rengolin
quelle
quelle
Antworten:
Angenommen, Sie
a
möchten den vorherigen Wert des Objekts, auf das verwiesen wird , überschreiben , muss eine Elementfunktion aufgerufen werden.In C ++ weist dieser Ausdruck den Compiler an, drei (3) Objekte auf dem Stapel zu erstellen, eine Addition durchzuführen und den resultierenden Wert aus dem temporären Objekt in das vorhandene Objekt zu kopieren
a
.Führt in Java
operator=
jedoch keine Wertekopie für Referenztypen durch, und Benutzer können nur neue Referenztypen erstellen, keine Werttypen. Für einen benutzerdefinierten Typ mit dem NamenComplex
bedeutet Zuweisung, einen Verweis auf einen vorhandenen Wert zu kopieren.Betrachten Sie stattdessen:
In C ++ kopiert dies den Wert, sodass der Vergleich ungleich ist. Führt in Java
operator=
eine Referenzkopie durcha
undb
verweist nun auf denselben Wert. Infolgedessen ergibt der Vergleich "gleich", da das Objekt gleich mit sich selbst vergleicht.Der Unterschied zwischen Kopien und Referenzen trägt nur zur Verwirrung bei der Überladung des Bedieners bei. Wie @Sebastian erwähnt hat, müssen Java und C # beide Wert- und Referenzgleichheit getrennt behandeln -
operator+
würden wahrscheinlich Werte und Objekte behandeln, sind jedochoperator=
bereits implementiert, um Referenzen zu behandeln.In C ++ sollten Sie sich jeweils nur mit einer Art von Vergleich befassen, damit dies weniger verwirrend sein kann. Zum Beispiel auf
Complex
,operator=
undoperator==
sind beide arbeiten an Werten - Kopieren Werte und Werte zu vergleichen sind.quelle
Es gibt viele Posts, die sich über die Überlastung des Bedieners beschweren.
Ich hatte das Gefühl, ich musste die Konzepte der "Operatorüberladung" klarstellen und einen alternativen Standpunkt zu diesem Konzept anbieten.
Code verschleiert?
Dieses Argument ist ein Irrtum.
Verschleierung ist in allen Sprachen möglich ...
Es ist genauso einfach, Code in C oder Java durch Funktionen / Methoden zu verschleiern wie in C ++ durch Operatorüberladungen:
... auch in Javas Standardschnittstellen
Ein weiteres Beispiel ist die
Cloneable
Benutzeroberfläche in Java:Sie sollen das Objekt klonen, das diese Schnittstelle implementiert. Aber du könntest lügen. Und erstellen Sie ein anderes Objekt. Tatsächlich ist diese Schnittstelle so schwach, dass Sie nur zum Spaß einen anderen Objekttyp zurückgeben können:
Sollte die
Cloneable
Schnittstelle, da sie missbraucht / verschleiert werden kann, aus denselben Gründen gesperrt werden, aus denen eine Überladung des C ++ - Operators bestehen soll?Wir könnten die
toString()
Methode einerMyComplexNumber
Klasse überladen , damit sie die festgelegte Stunde des Tages zurückgibt. Sollte dietoString()
Überladung auch verboten werden? Wir könnten sabotierenMyComplexNumber.equals
, damit es einen zufälligen Wert zurückgibt, die Operanden modifiziert ... usw. usw. usw.In Java, wie in C ++ oder einer anderen Sprache, muss der Programmierer beim Schreiben von Code ein Minimum an Semantik beachten. Dies bedeutet,
add
dass eine hinzugefügte FunktionCloneable
und eine++
klonende Implementierungsmethode sowie ein Operator als Inkremente implementiert werden .Was ist überhaupt verschleiert?
Jetzt, da wir wissen, dass Code selbst mit den makellosen Java-Methoden sabotiert werden kann, können wir uns fragen, wie die Operatorüberladung in C ++ tatsächlich eingesetzt wird.
Klare und natürliche Notation: Methoden vs. Überlastung des Bedieners?
Wir werden im Folgenden für verschiedene Fälle den "gleichen" Code in Java und C ++ vergleichen, um eine Vorstellung davon zu bekommen, welche Art von Codierungsstil klarer ist.
Natürliche Vergleiche:
Bitte beachten Sie, dass A und B in C ++ von jedem Typ sein können, solange die Operatorüberladungen bereitgestellt werden. Wenn in Java A und B keine Grundelemente sind, kann der Code selbst für primitivähnliche Objekte (BigInteger usw.) Sehr verwirrend werden.
Natürliche Array- / Container-Accessoren und Subskription:
In Java sehen wir, dass jeder Container, der dasselbe tut (über einen Index oder eine Kennung auf seinen Inhalt zugreift), eine andere Methode hat, was verwirrend ist.
In C ++ verwendet jeder Container dank der Überladung des Operators dieselbe Methode, um auf seinen Inhalt zuzugreifen.
Natürliche Manipulation fortgeschrittener Typen
In den folgenden Beispielen wird ein
Matrix
Objekt verwendet, das mithilfe der ersten bei Google gefundenen Links für " Java Matrix-Objekt " und " C ++ Matrix-Objekt " gefunden wurde:Und das ist nicht auf Matrizen beschränkt. Die
BigInteger
undBigDecimal
Klassen von Java leiden unter der gleichen verwirrenden Ausführlichkeit, während ihre Entsprechungen in C ++ so klar sind wie die eingebauten Typen.Natürliche Iteratoren:
Natürliche Funktoren:
Textverkettung:
Ok, in Java können Sie auch verwenden
MyString = "Hello " + 25 + " World" ;
... Aber warten Sie eine Sekunde: Dies ist eine Überladung des Operators, nicht wahr? Betrügt es nicht ???:-D
Generischer Code?
Dieselben generischen Code-Änderungsoperanden sollten sowohl für integrierte / primitive Elemente (die in Java keine Schnittstellen haben) als auch für Standardobjekte (die möglicherweise nicht die richtige Schnittstelle haben) und benutzerdefinierte Objekte verwendet werden können.
Berechnen Sie beispielsweise den Durchschnittswert von zwei Werten beliebiger Typen:
Überlastung des Bedieners besprechen
Nachdem wir faire Vergleiche zwischen C ++ - Code mit Operatorüberladung und demselben Code in Java gesehen haben, können wir nun "Operatorüberladung" als Konzept diskutieren.
Operatorüberlastung bestand seit vor Computern
Auch außerhalb der Informatik gibt es Überladen von Operatoren: Zum Beispiel in der Mathematik, Operatoren wie
+
,-
,*
usw. überlastet sind.Tatsächlich ist die Bedeutung von
+
,-
,*
usw. ändert sich in Abhängigkeit von den Typen der Operanden (Numerik, Vektoren, Quantenwellenfunktionen, Matrizes, etc.).Die meisten von uns haben im Rahmen ihrer naturwissenschaftlichen Kurse je nach Art der Operanden mehrere Bedeutungen für Operatoren gelernt. Fanden wir sie verwirrend?
Die Überladung des Operators hängt von seinen Operanden ab
Dies ist der wichtigste Teil der Überladung von Operatoren: Wie in der Mathematik oder in der Physik hängt die Operation von den Typen ihrer Operanden ab.
Kennen Sie also den Typ des Operanden, und Sie kennen die Auswirkung der Operation.
Sogar C und Java haben eine (fest codierte) Operatorüberladung
In C ändert sich das tatsächliche Verhalten eines Operators entsprechend seinen Operanden. Das Hinzufügen von zwei Ganzzahlen unterscheidet sich beispielsweise vom Hinzufügen von zwei Doppelten oder sogar einer Ganzzahl und einem Doppel. Es gibt sogar die gesamte Zeigerarithmetikdomäne (ohne Umwandlung können Sie einem Zeiger eine Ganzzahl hinzufügen, aber Sie können nicht zwei Zeiger hinzufügen ...).
In Java gibt es keine Zeigerarithmetik, aber jemand, der immer noch eine Verkettung von Zeichenfolgen ohne den
+
Operator gefunden hat, wäre lächerlich genug, um eine Ausnahme im Credo "Überladen von Operatoren ist böse" zu rechtfertigen.Es ist nur so, dass Sie als C- Codierer (aus historischen Gründen) oder Java- Codierer (aus persönlichen Gründen , siehe unten) keinen eigenen bereitstellen können.
In C ++ ist das Überladen von Operatoren nicht optional ...
In C ++ ist eine Operatorüberladung für integrierte Typen nicht möglich (und dies ist eine gute Sache), aber benutzerdefinierte Typen können benutzerdefinierte Operatorüberladungen aufweisen.
Wie bereits erwähnt, werden Benutzertypen in C ++ und im Gegensatz zu Java im Vergleich zu integrierten Typen nicht als Bürger zweiter Klasse der Sprache betrachtet. Wenn integrierte Typen Operatoren haben, sollten Benutzertypen diese auch haben können.
Die Wahrheit ist , dass, wie die
toString()
,clone()
,equals()
Methoden sind für Java ( dh quasi-Standard-like ), C ++ Überladen von Operatoren sind so sehr Teil von C ++ , dass es wie die Original - C - Operatoren als natürliches wird, oder die zuvor genannte Java - Methoden.In Kombination mit der Vorlagenprogrammierung wird das Überladen von Bedienern zu einem bekannten Entwurfsmuster. Tatsächlich können Sie in STL nicht sehr weit gehen, ohne überladene Operatoren und überladene Operatoren für Ihre eigene Klasse zu verwenden.
... aber es sollte nicht missbraucht werden
Die Überladung des Bedieners sollte sich bemühen, die Semantik des Bedieners zu respektieren. Subtrahieren Sie nicht in einem
+
Operator (wie in "Nicht in eineradd
Funktion subtrahieren " oder "Mist in einerclone
Methode zurückgeben").Überladung des Gipsverbandes kann sehr gefährlich sein, da sie zu Unklarheiten führen kann. Sie sollten also wirklich genau definierten Fällen vorbehalten sein. Was
&&
und||
, nicht überlastet sie nicht immer , wenn Sie wirklich wissen , was Sie tun, wie Sie die Auswertung des Kurzschlusses , dass der einheimischen Betreiber verlieren würden&&
und||
genießen.Also ... Ok ... warum ist es dann in Java nicht möglich?
Weil James Gosling es gesagt hat:
Bitte vergleichen Sie Goslings Text oben mit Stroustrups unten:
Würde eine Überlastung des Bedieners Java zugute kommen?
Einige Objekte würden stark von einer Überladung des Operators profitieren (konkrete oder numerische Typen wie BigDecimal, komplexe Zahlen, Matrizen, Container, Iteratoren, Komparatoren, Parser usw.).
In C ++ können Sie aufgrund der Demut von Stroustrup von diesem Vorteil profitieren. In Java werden Sie einfach wegen Goslings persönlicher Wahl verarscht .
Könnte es zu Java hinzugefügt werden?
Die Gründe dafür, dass in Java keine Operatorüberlastung hinzugefügt wird, könnten eine Mischung aus interner Politik, Allergie gegen die Funktion, Misstrauen gegenüber Entwicklern (Sie wissen, die Saboteure, die Java-Teams zu verfolgen scheinen ...) und Kompatibilität mit den vorherigen JVMs sein. Zeit, eine korrekte Spezifikation zu schreiben, etc ..
Halten Sie also nicht den Atem an und warten Sie auf diese Funktion ...
Aber sie machen es in C # !!!
Ja...
Während dies bei weitem nicht der einzige Unterschied zwischen den beiden Sprachen ist, amüsiert mich diese immer wieder.
Anscheinend haben die C # -Leute mit ihrem "jedes Primitiv ist ein
struct
und einstruct
stammt von Object" es beim ersten Versuch richtig gemacht.Und sie machen es in anderen Sprachen !!!
Trotz aller FUD gegen das Überladen definierter Operatoren wird es von folgenden Sprachen unterstützt: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...
So viele Sprachen mit so vielen verschiedenen (und manchmal gegensätzlichen) Philosophien, und doch sind sich alle in diesem Punkt einig.
Denkanstöße ...
quelle
James Gosling verglich das Entwerfen von Java mit folgendem:
Den Kontext des Zitats können Sie hier lesen
Grundsätzlich eignet sich die Überladung von Operatoren hervorragend für eine Klasse, die eine Art Punkt, Währung oder komplexe Zahl modelliert. Aber danach gehen Ihnen schnell die Beispiele aus.
Ein weiterer Faktor war der Missbrauch der Funktion in C ++ durch Entwickler, die Operatoren wie '&&', '||', die Cast-Operatoren und natürlich 'new' überladen. Die Komplexität, die sich aus der Kombination mit Wertübergabe und Ausnahmen ergibt, wird im Buch Exceptional C ++ ausführlich behandelt.
quelle
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)
.I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language
: Schlechte Programmierer schreiben schlechten Code, unabhängig von der Sprache. Versuchen Sie einfach, eine "Pass-by-Reference" für Funktionsparameter in Java zu emulieren, um eine Idee zu haben. Ich habe den Code gesehen und so heftig gelacht, dass es weh tat. Dies ist die Art von Dingen, die Gosling nicht verwendet hat, daher brauchte er schreckliche Hacks in Java, existiert jedoch nativ und ohne Kosten sowohl in C # als auch in C ++.Schauen Sie sich Boost.Units an: Linktext
Es bietet eine Dimensionsanalyse ohne Overhead durch Überlastung des Bedieners. Wie viel klarer kann das werden?
würde tatsächlich "Energie = 4 J" ausgeben, was korrekt ist.
quelle
Die Java-Designer entschieden, dass das Überladen von Operatoren mehr Probleme bereitete als es wert war. So einfach ist das.
In einer Sprache, in der jede Objektvariable tatsächlich eine Referenz ist, besteht für das Überladen von Operatoren die zusätzliche Gefahr, dass sie ziemlich unlogisch ist - zumindest für einen C ++ - Programmierer. Vergleichen Sie die Situation mit dem Überladen des Operators C # ==
Object.Equals
undObject.ReferenceEquals
(oder wie auch immer es heißt).quelle
Groovy hat eine Überladung des Bedieners und läuft in der JVM. Wenn Ihnen der Leistungseinbruch nichts ausmacht (der jeden Tag kleiner wird). Es basiert automatisch auf Methodennamen. Beispiel: '+' ruft die Methode 'plus (Argument)' auf.
quelle
where ...
wird.Where(i => ...
). Wenn sie nur dasselbe mit arithmetischen Operatoren gemacht hätten, wären so viele Dinge einfacher und leistungsfähiger. Java hat den Vorteil eines sauberen Schiefers und könnte dies richtig machen (obwohl dies aus religiösen Gründen wahrscheinlich niemals der Fall sein wird).Ich denke, dies war möglicherweise eine bewusste Designentscheidung, um Entwickler zu zwingen, Funktionen zu erstellen, deren Namen ihre Absichten klar kommunizieren. In C ++ überladen Entwickler Entwickler Operatoren mit Funktionen, die häufig keinen Bezug zur allgemein akzeptierten Natur des angegebenen Operators haben, was es nahezu unmöglich macht, zu bestimmen, was ein Code tut, ohne die Definition des Operators zu betrachten.
quelle
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator
: Dies ist eine unbegründete Behauptung. Ich bin seit 12 Jahren ein professioneller C ++ - Entwickler und bin selten auf dieses Problem gestoßen. Tatsächlich waren die meisten Fehler und Designfehler, die ich in C ++ gesehen habe, im C-Code (void *
, Casts usw.)add
Funktion wirklich wirklich missbraucht werden könnte (wie eine Multiplikation oder ein Mutex) ... Der von user14128 erwähnte Missbrauch ist nicht auf Operatoren beschränkt, sondern Es gibt eine Art pathologische Angst vor einer Überladung von Operatoren, von der ich glaube, dass sie aus den früheren Tagen von C vs. C ++ stammt, eine Angst, die direkt in Java unverändert blieb, aber zum Glück nicht in C # ging ... Am Ende wurde die Semantik respektiert Das Schreiben klarer Funktionen / Operatoren ist Aufgabe des Entwicklers. Nicht die Sprache.cout << f() || g();
Die Klammern machen es nicht klarer, sie machen es richtig. Und wenn die Bitverschiebungsoperatoren nicht missbraucht würden, wären sie nicht notwendig. Warum istcout << (5&3) << endl;
besser alscout.fmt(5&3)(endl);
? Die Verwendung des Funktionsaufrufoperators für eine Funktormitgliedvariable wäre ein unendlich besseres Design für Streams als die Verwendung von bitweisen Operatoren, nur weil die Glyphe gut aussieht. Dies ist jedoch bei weitem nicht das einzige, was mit Streams nicht stimmt.Nun, Sie können sich bei Überlastung des Bedieners wirklich in den Fuß schießen. Es ist wie mit Zeigern, die Leute machen dumme Fehler mit ihnen und so wurde beschlossen, die Schere wegzunehmen.
Zumindest denke ich, dass das der Grund ist. Ich bin sowieso auf deiner Seite. :) :)
quelle
Einige Leute sagen, dass das Überladen von Operatoren in Java zu einer Verschleierung führen würde. Haben diese Leute jemals aufgehört, sich einen Java-Code anzusehen, der einige grundlegende Berechnungen wie die Erhöhung eines finanziellen Werts um einen Prozentsatz mit BigDecimal durchführt? .... die Ausführlichkeit einer solchen Übung wird zu einer eigenen Demonstration der Verschleierung. Ironischerweise würde das Hinzufügen einer Operatorüberladung zu Java es uns ermöglichen, eine eigene Währungsklasse zu erstellen, die einen solchen mathematischen Code elegant und einfach (weniger verschleiert) macht.
quelle
Zu sagen, dass das Überladen von Operatoren zu logischen Fehlern führt, deren Operator nicht mit der Operationslogik übereinstimmt, ist wie nichts zu sagen. Dieselbe Art von Fehler tritt auf, wenn der Funktionsname für die Operationslogik ungeeignet ist. Was ist also die Lösung: Löschen Sie die Fähigkeit zur Funktionsnutzung!? Dies ist eine komische Antwort - "Unangemessen für die Operationslogik", jeder Parametername, jede Klasse, Funktion oder was auch immer logisch unangemessen sein kann. Ich denke, dass diese Option in einer respektablen Programmiersprache verfügbar sein sollte, und diejenigen, die denken, dass sie unsicher ist - hey, kein Bothy sagt, dass Sie sie verwenden müssen. Nehmen wir das C #. Sie haben die Zeiger hängen gelassen, aber hey - es gibt eine Anweisung für unsicheren Code - programmieren Sie, wie Sie möchten, auf eigenes Risiko.
quelle
Technisch gesehen gibt es in jeder Programmiersprache eine Überladung des Operators, die mit verschiedenen Arten von Zahlen umgehen kann, z. B. ganzzahlige und reelle Zahlen. Erläuterung: Der Begriff Überladung bedeutet, dass es einfach mehrere Implementierungen für eine Funktion gibt. In den meisten Programmiersprachen werden unterschiedliche Implementierungen für den Operator + bereitgestellt, eine für Ganzzahlen, eine für Real, dies wird als Operatorüberladung bezeichnet.
Nun, viele Leute finden es seltsam, dass Java eine Operatorüberladung für den Operator + hat, um Zeichenfolgen zusammenzufügen, und aus mathematischer Sicht wäre dies zwar seltsam, aber aus Sicht des Entwicklers einer Programmiersprache ist es nichts Falsches, eine eingebaute Operatorüberladung hinzuzufügen für den Operator + für andere Klassen zB String. Die meisten Leute sind sich jedoch einig, dass es im Allgemeinen eine gute Idee ist, diese Funktionalität auch für den Entwickler bereitzustellen, sobald Sie eine integrierte Überladung für + für String hinzugefügt haben.
Ein völliger Widerspruch zu dem Irrtum, dass das Überladen von Bedienern den Code verschleiert, da dies dem Entwickler überlassen bleibt. Das ist naiv zu denken und um ganz ehrlich zu sein, es wird alt.
+1 zum Hinzufügen einer Operatorüberladung in Java 8.
quelle
+
um irgendetwas String-ish zu verketten, ist meiner Meinung nach ziemlich abscheulich, ebenso wie die Überladung von/
C und FORTRAN für die vollständige und gebrochene Teilung. In vielen Versionen von Pascal führt die Verwendung von arithmetischen Operatoren für einen beliebigen numerischen Typ zu Ergebnissen, die numerisch dem Umwandeln der Operanden entsprechenReal
, obwohl Ergebnisse, die möglicherweise keine ganzen Zahlen sind, durchlaufen werden müssenTrunc
oderRound
bevor sie Ganzzahlen zugewiesen werden können.Unter der Annahme von Java als Implementierungssprache wären a, b und c Verweise auf den Typ Complex mit den Anfangswerten null. Unter der Annahme, dass Complex als die erwähnte BigInteger und ähnliche unveränderliche BigDecimal unveränderlich ist , würde ich Folgendes meinen, wenn Sie die Referenz dem Komplex zuweisen, der durch Hinzufügen von b und c zurückgegeben wurde, und diese Referenz nicht mit a vergleichen.
quelle
Manchmal wäre es schön, wenn Operatoren überladen würden, Freundklassen und Mehrfachvererbung.
Ich denke jedoch immer noch, dass es eine gute Entscheidung war. Wenn Java eine Überladung von Operatoren gehabt hätte, könnten wir uns der Bedeutung von Operatoren niemals sicher sein, ohne den Quellcode durchzusehen. Derzeit ist das nicht notwendig. Und ich denke, Ihr Beispiel für die Verwendung von Methoden anstelle von Operatorüberladung ist auch gut lesbar. Wenn Sie die Dinge klarer machen möchten, können Sie jederzeit einen Kommentar über haarigen Aussagen hinzufügen.
quelle
Dies ist kein guter Grund, es zu verbieten, aber ein praktischer:
Die Leute gehen nicht immer verantwortungsbewusst damit um. Schauen Sie sich dieses Beispiel aus der Python-Bibliothek an:
Hier ist die Erklärung:
quelle
Alternativen zur nativen Unterstützung von Java Operator Overloading
Da Java keine Operatorüberladung aufweist, können Sie folgende Alternativen prüfen:
Wenn jemand andere kennt, kommentieren Sie diese bitte und ich werde sie dieser Liste hinzufügen.
quelle
Während die Java-Sprache das Überladen von Operatoren nicht direkt unterstützt, können Sie das Manifold-Compiler-Plugin in jedem Java-Projekt verwenden, um es zu aktivieren. Es unterstützt Java 8 - 13 (die aktuelle Java-Version) und wird in IntelliJ IDEA vollständig unterstützt.
quelle