Warum bietet Java keine Operatorüberladung an?

406

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?

Rengolin
quelle
6
Kann
1
@zzzz, es fällt mir schwer, diesen Artikel zu lesen. Wurde dies automatisch übersetzt oder ist Englisch die zweite Sprache des Schriftstellers? Ich finde die Diskussion hier viel sauberer.
25
Für die Leute, die dies als nicht konstruktiv abschließen, hat diese Frage einige der konstruktivsten Dialoge ergeben, die ich bei SO gesehen habe. Vielleicht ist es ein besserer Kandidat für programmers.stackexchange.com , aber es gibt Zeiten, in denen ich denke, dass SO breitere Themen übermäßig ablehnt.
@NoNaMe es ist einfach, nur geistig einfügen ein und die - fehlende artlcles ein untrügliches Zeichen ist , dass die Person entweder nicht Englisch als Muttersprache ist oder ein Programmierer (oder wie dieser Typ, beide :) Der Grund , warum Programmierer Artikel fallen kann , ist , dass es Machen Sie Kommentare kürzer und passen Sie sie leichter in den dafür vorgesehenen Raum. Von dort aus gewöhnen sie sich einfach daran. Mein Problem ist mit dem Layout, irgendwie treffe ich diese Seite immer in Google-Suchen. Glücklicherweise gibt es eine großartige Chrome-Erweiterung namens " Klar" , die schwer lesbare Seiten wunderbar neu formatiert.
ycomp
1
Ich sehe keinen Grund, warum und wie OP die erste Antwort akzeptiert hat. Die Antwort von @ stackoverflow.com/users/14089/paercebal ist ausgezeichnet. Es sollte akzeptiert werden.
Zerstörer

Antworten:

13

Angenommen, Sie amöchten den vorherigen Wert des Objekts, auf das verwiesen wird , überschreiben , muss eine Elementfunktion aufgerufen werden.

Complex a, b, c;
// ...
a = b.add(c);

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 kopierena .

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 Namen Complexbedeutet Zuweisung, einen Verweis auf einen vorhandenen Wert zu kopieren.

Betrachten Sie stattdessen:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail

In C ++ kopiert dies den Wert, sodass der Vergleich ungleich ist. Führt in Java operator=eine Referenzkopie durch aund bverweist 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 jedoch operator=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=und operator==sind beide arbeiten an Werten - Kopieren Werte und Werte zu vergleichen sind.

Aaron
quelle
6
Es ist wirklich ziemlich einfach ... Mag Python einfach und habe keine überladene Aufgabe.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
225
Diese Antwort beantwortet die Frage überhaupt nicht. Sie spielen einfach mit Javas Verwendung des Gleichheitszeichens. Wenn b + C einen neuen Komplex zurückgeben würde, wäre a = b + c vollkommen gültig und ja viel einfacher zu lesen. Selbst wenn Sie a an Ort und Stelle ändern möchten, ist a.set (b + c) eine Tonne einfacher zu lesen - insbesondere, wenn die Arithmetik mehr als trivial ist: a.set ((a b + b c) / 5) oder a = a. Multiplizieren (b) .add (b. Multiplizieren (c)). Teilen (5). Ihre Wahl ..
BT
24
Oder ich denke .. nicht Ihre Wahl, wie der Fall sein kann
BT
9
In C ++ lösen Ausdrucksvorlagen das Problem der zusätzlichen Kopie. Nahezu alle großen arithmetischen Bibliotheken verwenden diese Technik aus genau diesem Grund. Dies geht auch nicht auf die Frage ein, da a = b + c nur syntaktischer Zucker für a.foo (b.bar (c)) ist, was eigentlich die erste Beobachtung in der Frage ist.
Kaz Dragon
18
Dies ist nicht die Antwort auf die gestellte Frage. Dies sind Spekulationen über bestimmte Unterschiede zwischen Java und C ++.
SChepurin
804

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:

// C++
T operator + (const T & a, const T & b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

// Java
static T add (T a, T b) // add ?
{
   T c = new T() ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

/* C */
T add (T a, T b) /* add ? */
{
   T c ;
   c.value = a.value - b.value ; /* subtract !!! */
   return c ;
}

... auch in Javas Standardschnittstellen

Ein weiteres Beispiel ist die CloneableBenutzeroberflä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:

class MySincereHandShake implements Cloneable
{
    public Object clone()
    {
       return new MyVengefulKickInYourHead() ;
    }
}

Sollte die CloneableSchnittstelle, 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 einer MyComplexNumberKlasse überladen , damit sie die festgelegte Stunde des Tages zurückgibt. Sollte die toString()Überladung auch verboten werden? Wir könnten sabotieren MyComplexNumber.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, adddass eine hinzugefügte Funktion Cloneableund 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:

// C++ comparison for built-ins and user-defined types
bool    isEqual          = A == B ;
bool    isNotEqual       = A != B ;
bool    isLesser         = A <  B ;
bool    isLesserOrEqual  = A <= B ;

// Java comparison for user-defined types
boolean isEqual          = A.equals(B) ;
boolean isNotEqual       = ! A.equals(B) ;
boolean isLesser         = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual  = A.comparesTo(B) <= 0 ;

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:

// C++ container accessors, more natural
value        = myArray[25] ;         // subscript operator
value        = myVector[25] ;        // subscript operator
value        = myString[25] ;        // subscript operator
value        = myMap["25"] ;         // subscript operator
myArray[25]  = value ;               // subscript operator
myVector[25] = value ;               // subscript operator
myString[25] = value ;               // subscript operator
myMap["25"]  = value ;               // subscript operator

// Java container accessors, each one has its special notation
value        = myArray[25] ;         // subscript operator
value        = myVector.get(25) ;    // method get
value        = myString.charAt(25) ; // method charAt
value        = myMap.get("25") ;     // method get
myArray[25]  = value ;               // subscript operator
myVector.set(25, value) ;            // method set
myMap.put("25", value) ;             // method put

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 MatrixObjekt verwendet, das mithilfe der ersten bei Google gefundenen Links für " Java Matrix-Objekt " und " C ++ Matrix-Objekt " gefunden wurde:

// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E =  A * (B / 2) ;
E += (A - B) * (C + D) ;
F =  E ;                  // deep copy of the matrix

// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ;            // deep copy of the matrix

Und das ist nicht auf Matrizen beschränkt. Die BigIntegerund BigDecimalKlassen 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:

// C++ Random Access iterators
++it ;                  // move to the next item
--it ;                  // move to the previous item
it += 5 ;               // move to the next 5th item (random access)
value = *it ;           // gets the value of the current item
*it = 3.1415 ;          // sets the value 3.1415 to the current item
(*it).foo() ;           // call method foo() of the current item

// Java ListIterator<E> "bi-directional" iterators
value = it.next() ;     // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ;        // sets the value 3.1415 to the current item

Natürliche Funktoren:

// C++ Functors
myFunctorObject("Hello World", 42) ;

// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;

Textverkettung:

// C++ stream handling (with the << operator)
                    stringStream   << "Hello " << 25 << " World" ;
                    fileStream     << "Hello " << 25 << " World" ;
                    outputStream   << "Hello " << 25 << " World" ;
                    networkStream  << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;

// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;

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:

// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
   return (p_lhs + p_rhs) / 2 ;
}

int     intValue     = getAverage(25, 42) ;
double  doubleValue  = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix  matrixValue  = getAverage(mA, mB) ; // mA, mB are Matrix

// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.

Ü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 einer addFunktion subtrahieren " oder "Mist in einer cloneMethode 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:

Ich habe das Überladen von Operatoren als ziemlich persönliche Wahl ausgelassen, weil ich zu viele Leute gesehen habe, die es in C ++ missbraucht haben.

James Gosling. Quelle: http://www.gotw.ca/publications/c_family_interview.htm

Bitte vergleichen Sie Goslings Text oben mit Stroustrups unten:

Viele C ++ - Entwurfsentscheidungen haben ihre Wurzeln in meiner Abneigung, Menschen dazu zu zwingen, Dinge auf eine bestimmte Art und Weise zu tun. [...] Oft war ich versucht, eine Funktion zu verbieten, die ich persönlich nicht mochte. Ich verzichtete darauf, weil ich nicht glaubte, dass ich sie hatte das Recht, meine Ansichten anderen aufzuzwingen .

Bjarne Stroustrup. Quelle: Das Design und die Entwicklung von C ++ (1.3 Allgemeiner Hintergrund)

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 structund ein structstammt 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 ...

paercebal
quelle
50
Dies ist eine ausgezeichnete Antwort. Ich bin damit nicht einverstanden, aber es ist immer noch eine ausgezeichnete Antwort. Ich denke, die Probleme, die bei schlechten Überlastungen möglich sind, überschreiten den Wert der guten Überlastungen.
Douglas Leeder
69
@ Douglas Leeder: Danke! Die Überlastung des Bedieners ist wie bei OOP. Wenn Sie dies zum ersten Mal lernen, schreiben Sie überall Überladungen, da Sie überall Basisklassen und Vererbung platzieren würden (wie süße Ironie, die Java-API). Aber das geht ziemlich schnell vorbei und dann schätzt du die Möglichkeit, ohne sie zu missbrauchen. Meine eigene 10-jährige Erfahrung mit C ++ ist, dass die Anzahl der fehlerhaften Überladungen, die ich sowohl in meinem Code als auch im Code anderer Codierer gesehen habe, so gering ist, dass ich glaube, ich könnte sie einerseits zählen. Und dies ist viel weniger als die Anzahl der Gesamtfehler mit Sprintf-, Strcat-, Memset- und Pufferüberschreitungen.
Paercebal
11
@Douglas Leeder: Ich glaube, nachdem ich darüber in einer anderen SO-Frage diskutiert habe, dass die Kluft zwischen "Liebhabern" und "Hassern" der Operatorüberlastung wahrscheinlich durch einen Unterschied in ihrer Herangehensweise an den Code verursacht wird: "Hasser" sind mehr "Funktionen" sind das, worauf es ankommt ", was bedeutet, dass sie erwarten, dass eine Funktion nur eine Sache tut. Daher sollten die Bediener so arbeiten, wie es die Sprache vorsieht. Bei "Liebhabern" geht es eher um "Objekte sollten sich verhalten", was bedeutet, dass sie leichter akzeptieren, dass Funktionen (und damit Operatoren) ihr Verhalten entsprechend der Art ihrer Parameter ändern können.
Paercebal
103
Epische Antwort. Eines der qualifiziertesten Debunks, das ich je gelesen habe.
Sebastian Mach
7
@ MaartenBodewes: Alle Beispiele, die ich oben geschrieben habe, und alles, was Sie stört, ist "Als Entwickler sind Sie wegen Goslings persönlicher Wahl durcheinander " ? Bitte schreiben Sie Ihre eigene Antwort und verteidigen Sie den Blickwinkel "Sie Entwickler sind dumm, lassen Sie die genialen Leute für Sie entscheiden, was Sie brauchen" . Diese Diskussion hat keinen Zweck.
Paercebal
44

James Gosling verglich das Entwerfen von Java mit folgendem:

"Es gibt dieses Prinzip beim Umzug, wenn Sie von einer Wohnung in eine andere ziehen. Ein interessantes Experiment besteht darin, Ihre Wohnung einzupacken und alles in Kisten zu packen, dann in die nächste Wohnung zu ziehen und nichts auszupacken, bis Sie es brauchen. Wenn Sie Ihre erste Mahlzeit zubereiten und etwas aus einer Schachtel ziehen, haben Sie nach etwa einem Monat damit herausgefunden, welche Dinge in Ihrem Leben Sie tatsächlich brauchen, und dann nehmen Sie den Rest der Mahlzeit Sachen - vergiss, wie sehr du es magst oder wie cool es ist - und wirfst es einfach weg. Es ist erstaunlich, wie das dein Leben vereinfacht, und du kannst dieses Prinzip bei allen Arten von Designproblemen anwenden: Dinge nicht nur tun, weil sie es tun sind cool oder nur weil sie interessant sind. "

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.

Garth Gilmour
quelle
6
Könnten Sie ein Codebeispiel für "die Komplexität der Überladung von Operatoren in Kombination mit Wertübergabe und Ausnahmen" angeben? Obwohl ich einige Jahre mit der Sprache gespielt habe und alle effektiven / außergewöhnlichen Bücher über C ++ besessen und gelesen habe, verstehe ich nicht, was Sie damit meinen.
Paercebal
60
Was für James Gosling funktioniert, wird nicht für alle funktionieren. Er ist unglaublich kurzsichtig, weil er sein "interessantes" Verpackungsexperiment so hochgerechnet hat, dass es bedeutet: "Wirf alles auf der Welt weg, was ich nicht brauche, damit niemand dieses Zeug benutzen kann." Er weiß eindeutig nicht, was ich brauche oder benutze.
BT
49
@BT: Am aufschlussreichsten ist Goslings Standpunkt im Vergleich zu Stroustrups Standpunkt zu diesem Thema : 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).
Paercebal
29
@ Software Monkey: "C ++, weit verbreitet im Vergleich zu dem anderen, Java, weit verbreitet" Dies ist ein Marketing-Hype. Denken Sie daran, dass C ++ alleine gewachsen ist, während Java (und .NET) vom Marketing von Bulldozern profitiert haben. Ist es nicht seltsam, dass Java für eine "weit verbreitete Sprache" auf Serveranwendungen beschränkt ist, während "weit verbreitet" (wahrscheinlich von Java-Entwicklern und -Managern, die die Kosten für die Codeproduktion senken wollen) C ++ von sehr hoch ist? Leistungsserver zu Hochleistungsspielen? [...]
Paercebal
16
@ Hassan: Jede Sprache hat ihre Hacks, Javas Generika sind ein gutes Beispiel dafür. Nun zu 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 ++.
Paercebal
22

Schauen Sie sich Boost.Units an: Linktext

Es bietet eine Dimensionsanalyse ohne Overhead durch Überlastung des Bedieners. Wie viel klarer kann das werden?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

würde tatsächlich "Energie = 4 J" ausgeben, was korrekt ist.

user15793
quelle
1
"Wie genau, wenn die Wartung kompliziert ist und wo auf der Erde verschleiert dieser Code?"
Mooing Duck
13

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.Equalsund Object.ReferenceEquals(oder wie auch immer es heißt).

Sebastian Redl
quelle
8

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.

Noah
quelle
4
Ich wünschte, alle syntaxlastigen Sprachen mit Operatorüberladung hätten diese Technik verwendet. Ich habe nie verstanden, warum sie eine spezielle Version der Methodenbenennung und -suche erfinden müssen. Stroustrup erwähnt in D & EC ++ keine Alternativen. Das C # -Team hat mit der Linq-Syntax den richtigen Ansatz gewählt ( 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).
Daniel Earwicker
@ DanielEarwicker, ich habe oft bemerkt, dass Menschen bei komplizierten Meinungsverschiedenheiten die Motivationen für beide Seiten als "religiös" bezeichnen.
@noah, ich könnte mit einer begrenzten Teilmenge von Operatorüberladungen wie dieser leben, vorausgesetzt, die Methodennamen hatten ein spezielles Tag, das sie visuell voneinander unterscheidet. So etwas wie das Definieren einer __plus () -Methode für die Implementierung eines "+" OL und das Fernüberladen von Dingen wie Casts und sogar Array-Indizes. Ich bin nicht bereit, damit zu leben, wie C ++ und C # es für angebracht hielten, es zu implementieren.
2
Keine Antwort. Auf der VM werden viele Sprachen ausgeführt. Das Überladen von Operatoren sollte an sich kein guter Grund sein, die Sprache zu wechseln.
Maarten Bodewes
6

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.

user14128
quelle
14
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.)
paercebal
6
-1. Jede Variable, die Sie zuweisen, ist ein Symbol, genau wie die arithmetischen Operatorsymbole. Ob Sie eine Phrase verwenden, um diese Variable, ein einzelnes Wort oder einen einzelnen Buchstaben zu benennen, ist Ihre Entscheidung (oder die Ihres Teams). Wer soll sagen, was sinnvoll ist und was nicht? Die Antwort sind Sie, der Programmierer. In der reinen Mathematik bedeutet die Multiplikation zwischen Matrizen etwas anderes als die Multiplikation zwischen zwei Zahlen in der Grundrechenart. Wir verwenden jedoch für beide Multiplikationsarten dieselben Symbole.
Ingenieur
2
@paercebal: Die Behauptung ist leider richtig. Sie müssen nicht weiter als bis zu IOstreams schauen, um es in Aktion zu sehen. Zum Glück sind die meisten Entwickler umsichtiger, wenn es darum geht, neue Semantiken für bestehende Operatoren zu erfinden.
Ben Voigt
5
@BenVoigt: [...] Und ich erwähne nicht einmal die Tatsache, dass die addFunktion 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.
Paercebal
3
@ jbo5112: Beispiel: 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 ist cout << (5&3) << endl;besser als cout.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.
Ben Voigt
5

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

Sarien
quelle
Wie zum Beispiel dieser dumme Fehler ...
Amir Kirsh
2
Das ist eine sehr schlechte Denkweise. Sie können sich in den Fuß schießen, wir schneiden Ihnen lieber die Hände ab, damit Sie nicht dazu in der Lage sind. Und natürlich nehmen wir an, dass Sie ein Idiot sind, der sich selbst erschießen wird.
ntj
5

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.

Volksman
quelle
4

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.

Kvant
quelle
4

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.

Olai
quelle
Javas Verwendung, +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 entsprechen Real, obwohl Ergebnisse, die möglicherweise keine ganzen Zahlen sind, durchlaufen werden müssen Truncoder Roundbevor sie Ganzzahlen zugewiesen werden können.
Supercat
2

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.

Ist nicht:

Complex a, b, c; a = b + c;

viel einfacher als:

Complex a, b, c; a = b.add(c);
David Schlosnagle
quelle
2
Bin ich ;) Gleich kann sowohl Zuordnung als auch Vergleich bedeuten, aber = ist immer Zuordnung und == ist immer Vergleich. Namen können selbst große Fehlerquellen verursachen.
1

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.

// a = b + c
Complex a, b, c; a = b.add(c);

quelle
12
Natürlich können Sie, wie an anderer Stelle erwähnt, auch nie sicher sein, welche Bedeutung die Funktion add hat.
Eclipse
Es stimmt, ich finde es immer noch beruhigend zu wissen, dass zumindest meine Bediener fest codiert sind. Natürlich würde es uns nur gut tun, die Funktionen zu haben und sie sinnvoll zu nutzen. Das Problem ist, dass es schwer zu wissen ist, ob jemand sie vernünftig benutzt hat. Und dass Sie der Definition von vernünftig zustimmen. :-)
1
Der zur Verdeutlichung des Codes hinzugefügte Kommentar gibt an, wie der Code in einer Sprache aussehen würde, die das Überladen von Operatoren unterstützt. Darüber hinaus widerlegt die Tatsache, dass der Kommentar in Bezug auf die Betreiber geschrieben wurde, Ihre Ablehnung einer Überlastung der Betreiber.
Aluan Haddad
0

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:

>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>

Hier ist die Erklärung:

Der Operator / wurde als Kompositionsoperator zwischen zwei Ebenen verwendet. Dabei kann die untere Schicht eines oder mehrere ihrer Standardfelder entsprechend der oberen Schicht überladen haben. (Sie können immer noch den gewünschten Wert angeben). Eine Zeichenfolge kann als Rohebene verwendet werden.

Sarien
quelle
0

Alternativen zur nativen Unterstützung von Java Operator Overloading

Da Java keine Operatorüberladung aufweist, können Sie folgende Alternativen prüfen:

  1. Verwenden Sie eine andere Sprache. Sowohl Groovy als auch Scala sind vom Operator überladen und basieren auf Java.
  2. Verwenden Sie java-oo , ein Plugin, das das Überladen von Operatoren in Java ermöglicht. Beachten Sie, dass es NICHT plattformunabhängig ist. Außerdem hat es viele Probleme und ist nicht mit den neuesten Versionen von Java (dh Java 10) kompatibel. ( Original StackOverflow-Quelle )
  3. Verwenden Sie JNI , Java Native Interface oder Alternativen. Auf diese Weise können Sie C- oder C ++ - Methoden (möglicherweise andere?) Zur Verwendung in Java schreiben. Natürlich ist dies auch NICHT plattformunabhängig.

Wenn jemand andere kennt, kommentieren Sie diese bitte und ich werde sie dieser Liste hinzufügen.

gagarwa
quelle
0

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.

Scott
quelle