Was kann C# besser als Java?



  • Das ist ja auch nicht äquivalent. Mit compareTo kannst du auch die Fälle trennen, wo die Objekte gleich sind und wo obj1 > obj 2. Das kannst du mit dem "<" nicht. Du bräuchtest da also einen Operator mit einer dreiwertigen Logik, den es natürlich nicht gibt.



  • Gregor schrieb:

    1. Ja, momentan kann man neben Java nur 189 andere Sprachen nutzen, um Java-Bytecode zu erzeugen: Programming Languages for the Java Virtual Machine

    Und wer macht das?



  • Man hat Operator-Overloading in Java ja nicht deshalb nicht eingebaut, weil man es nicht könnte, sondern weil man es für schlecht hält.

    Stellt sich die Frage, wer genau "Man" ist und warum "Man" es für schlecht hält? James Gosling zumindest hat bereits im Jahr 2000 gesagt, dass das Fehlen von Operatorüberladung in Java seine Nachteile hat. So ist Java ohne Operatorüberladung z.B. für "Numerics"-Leute völlig uninteressant. Eine Sache die Gosling nicht so wirklich schmeckt.

    Gregor schrieb:

    MaSTaH schrieb:

    Das nennst du syntaktischen Zucker der nur Zeichen spart? Sowas kann doch nur von einem eingefleischten Javaiianer kommen 😉 .

    Naja, natürlich ist das meine persönliche Sichtweise. Ich halte ein

    c = a + b;
    

    statt einem

    c = a.add(b);
    

    für eine sehr geringe Veränderung. Das ändert nicht die Art, wie man programmiert, sondern erspart einem nur ein paar Zeichen.

    Es bleibt in der Regel wohl kaum bei a + b. Was ist zum Beispiel hiermit (ist C++):

    Z = add( add( R, mul( mul( j, omega ), L ) ),
    div( 1, mul( j, omega ), C ) ) );
    

    vs:

    Z = R + j*omega*L + 1/(j*omega*C);
    

    Wie schnell hast du erkannt, dass die erste Formel nicht korrekt ist? Und wie schnell hast du die Formel in der ersten Version überhaupt wiedererkannt?

    Operator-Überladung ist ein weiteres Abstraktionsmittel einer Sprache. Es hilft dabei neue Datentypen zu entwickeln, die eingebauten syntaktisch in nichts nachstehen. Diese Form der Transparenz ist manchmal doch durchaus erwünscht. Bibliotheken wie die Iostream-Lib von C++ zeigen eine weitere sinnvolle Anwendung von Operatorüberladung (Erweiterung eines syntaktischen Frameworks).

    Natürlich kann man mit OÜ auch viel unsinn betreiben, aber das gilt eigentlich für alle machtvollen Abstraktionsmittel.

    Letztlich ist OÜ imo entweder das fehlende Salz oder aber das ärgerliche Haar in der Suppe. Da wo man es braucht, braucht man es wirklich und wenn es fehlt, muss man hässliche Workarounds verwenden. Bricht man hingegen einmal die Regeln oder implementiert man einen Operator so neu, dass dessen Semantik nicht jedem wirklich klar ist, dann hat man mehr Ärger als Nutzen.



  • Gregor@UNI schrieb:

    Das ist ja auch nicht äquivalent. Mit compareTo kannst du auch die Fälle trennen, wo die Objekte gleich sind und wo obj1 > obj 2. Das kannst du mit dem "<" nicht. Du bräuchtest da also einen Operator mit einer dreiwertigen Logik, den es natürlich nicht gibt.

    hm. du meinst wohl sowas:

    int ret = obj1.compareTo(obj2);
    if (ret > 0) // bla
    else if (ret < 0) // blubb
    else // tralla
    

    hm, schön. ich glaub ich hab dich falsch verstanden :p ich jedenfalls verwende den vergleichsoperator immer spezifiziert:

    if (obj1 < obj2) // blubb
    else if (obj1 < obj) // trulla
    else // tralla
    

    oder was hab ich da jetz falsch verstanden? vielleicht dass man nicht 3 operatoren explizit deklarieren muss, sondern alle drein implementierungen in eine klatscht? wie gesagt, geschmackssache 🙂 ich hoff, java 1.5 kanns, weil ich java demnächst für ein größeres projekt hernehmen will

    edit: dito hume, schön argumentiert 🙂



  • btw. arbeitet glaube ich das dotGNU Projekt daran, einen dotNET-Assemblie-to-Java-ByteCode Compiler zu schreiben...



  • HumeSikkins schrieb:

    Natürlich kann man mit OÜ auch viel unsinn betreiben, aber das gilt eigentlich für alle machtvollen Abstraktionsmittel.

    Genau das habe ich nie verstanden. Inwiefern kann ich mit Operaoren Ueberladung mehr Bloedsinn machen als ich mit Funktionen/Methoden kann.

    Ich kann zwar den op+ zum dividieren verwenden, aber ich kann genauso eine Funktion
    writeToFile()
    lediglich einen Wert ausrechnen lassen.

    Kannst du mir das bitte erklaeren wie ich mit OUe unlesbarere Programme schreiben kann als mit bloeden Funktionsnamen?

    btw:
    c=a.add(b);
    wieso gehoert das add zu a? und warum nicht zu b?
    Was wenn das 2 verschiedene Typen sind? Es hat ja seinen Grund warum man den operator+ nicht als (non static) Member definiert 😉

    und ein
    Foo a;
    Bar b;
    c=Foo.add(a,b)
    ist ja auch komisch. Wieso gehoert das add denn zu Foo und nicht zu Bar??

    mich verwirrt sowas immer 😞



  • Shade Of Mine schrieb:

    btw:
    c=a.add(b);
    wieso gehoert das add zu a? und warum nicht zu b?

    Weil a der linksseitige Operand ist.

    Shade Of Mine schrieb:

    Was wenn das 2 verschiedene Typen sind?

    Was soll dann sein?



  • Sgt. Nukem schrieb:

    Shade Of Mine schrieb:

    btw:
    c=a.add(b);
    wieso gehoert das add zu a? und warum nicht zu b?

    Weil a der linksseitige Operand ist.

    Shade Of Mine schrieb:

    Was wenn das 2 verschiedene Typen sind?

    Was soll dann sein?

    Dann muss ich die Funktion 2 mal implementieren.
    einmal in Foo fuer Bar und einmal in Bar fuer Foo

    finde ich persoenlich ungut.
    vorallem wenn ein
    a.add(b) geht, aber ein b.add(a) nicht.

    weiters hat man ein namenproblem:
    ist add jetzt der op+ oder der op+=?



  • das ganze nähert sich der gretchenfrage :p
    ich fänds einfach schön, wenn java op-overloading hätte, dann würd ichs öfter einsetzen glaub ich.



  • @Shade
    Funktionen beinhalten an sich erstmal überhaupt keine Semantik. Sie haben ja erstmal keinen Namen. Diesen musst du erst explizit erfinden. Operatoren hingegen werden nicht explizit benannt. Sie haben ihren Namen (bzw. ihr Symbol) von Anfang an. Genauso wie eine (erwartete) Semantik.
    Das sind imo also zwei unterschiedliche Herangehensweisen. Auf der einen Seite entwickelst du ein bestimmtes Verhalten und gibst diesem danach explizit einen Namen (->Funktion). Auf der anderen Seite hast du einen Namen gegeben und entwickelst darein ein bestimmtes Verhalten (->Operator).

    Das Problem bei Operatoren ist die Geschichte mit der Semantik. Wenn die nicht allen 100% klar ist (wenn sich also nicht alle einig sind, was ein Operator genau bedeutet), spielst du ein Spiel mit der Intuation des Anwenders.
    Ein Beispiel (angelehnt an Dewhursts: "C++ Gotchas")

    template <class T, int size>
    class Array
    {
    public:
        ...
        Array& operator=(const T& val);
    };
    

    Hat der =-Operator hier wirklich nur eine Bedeutung die allen klar ist?

    Array<int, 50> arr;
    arr = 75;
    

    Für dich bedeutet diese Anweisung logischerweise, dass alle 50 Elemente von arr mit 75 belegt werden. Ein Anderer könnte aber denken, dass hier das Array auf 75 Elemente vergrößert wird. Und wieder andere finden, dass hier das erste Element mit 75 belegt wird.
    Drei mögliche Interpretationen für das selbe Zeichen -> nicht gut. Da mit einem Operator aber immer auch Intuition verbunden ist (die kennen wir schließlich schon aus der Schule), wird wahrscheinlich jeder denken, dass seine Intuition die Richtige ist.

    Inwiefern unterscheidet sich das jetzt von einer völlig falsch benannten Funktion:

    template <class T, int size>
    class Array
    {
    public:
        ...
        Array& assign(const T&); // schrecklicher Name. Besser wäre sowas wie setAll
    };
    
    Array<int, 50> arr;
    arr.assign(75);
    

    Meiner Meinung nach in einer eher technischen und einer eher emotionalen Hinsicht:
    arr.assign() ist direkt als Methodenaufruf zu identifizieren. Damit sehe ich sofort, in welchem Kontext ich nach einer Dokumentation suchen muss (die Syntax garantiert mir, dass hier keine unsichtbaren Konvertierungen auf dem linken Operand stattfinden können). Bei einem Operator hingegen wird das schneller mal verschleiert. Hinzukommend ist die Wahrscheinlichkeit, dass jemand in die Doku schaut bei einer Funktion deutlich höher, da man sich zum bei einer Funktion a) nicht so sehr auf seine Intuition verlässt (drei Funktionen die das gleiche machen, haben in drei Frameworks drei leicht verschiedene Namen. Man hat sich also nicht so sehr an einen Namen gewöhnt) und b) ist man es gewöhnt, dass die Semantik einer Funktion in einer API-Dokumentation genauer beschrieben wird.

    Letztlich ist die Frage aber nicht, ob schlechte Operatorüberladung schlimmer ist als falsch benannte Funktionen. Vielmehr geht es ja darum, dass Operatorüberladung dazu verleiten kann, dass du eine Funktionalität die ungefähr so ähnlich ist wie zuweisen in ein operator=-Korsett presst.
    Das kann dir mit Funktionen nicht so schnell passieren, da du hier ja *explizit* einen Namen vergeben musst. Und wenn du sowieso weißt, dass du "allen Elementen einen Wert X zuweisen" implementiert hast, dann kannst du deine Funktion auch *genau* passend benennen.
    Es gibt dummerweise aber nun mal keinen Operator "allen Elementen einen Wert X zuweisen".



  • Zusatz: Natürlich könnte man jetzt auch einfach sagen, dass der Nutzer einfach die Bedeutung des Operators in der Doku nachschlagen soll (nach dem Motto: Wer eine Funktion nachschlagen kann, der kann auch einen Operator nachschlagen). Das ist aber gerade nicht im Sinne des Erfinders und zerstört den wirklichen Vorteil von Operatorüberladung.
    Das ich statt 8 Zeichen nur eines eingeben muss, ist ja nicht wirklich relevant. Es geht ja gerade darum, dass ich es einem Anwender ermögliche, dass er sein ohnehin vorhandene Wissen intuitiv anwenden kann. Er will die komplexen Zahlen x und y addieren, schreibt x + y und muss nicht erst in der Doku nachschauen ob die Funktion nun add, Add oder gar addieren heißt.
    Muss die Semantik einer Operatorimplementierung erst nachgeschlagen werden, dann ist das imo ein unschlagbares Zeichen dafür, dass ich lieber keinen Operator überladen hätte.



  • HumeSikkins schrieb:

    Letztlich ist die Frage aber nicht, ob schlechte Operatorüberladung schlimmer ist als falsch benannte Funktionen. Vielmehr geht es ja darum, dass Operatorüberladung dazu verleiten kann, dass du eine Funktionalität die ungefähr so ähnlich ist wie zuweisen in ein operator=-Korsett presst.

    Mhm... so habe ich das noch nie gesehen.

    Das kann dir mit Funktionen nicht so schnell passieren, da du hier ja *explizit* einen Namen vergeben musst. Und wenn du sowieso weißt, dass du "allen Elementen einen Wert X zuweisen" implementiert hast, dann kannst du deine Funktion auch *genau* passend benennen.

    Wie sieht es jetzt aber aus, wenn ich interfaces habe. Wenn ich zB das interface Assignable habe, dann habe ich die Methode assign() die ich jetzt ueberladen muss. Dann ist dies doch sehr aehnlich zu einem op= oder? Denn ich habe ein Symbol dem ich ein Verhalten "zuweisen" muss.



  • Shade Of Mine schrieb:

    Das kann dir mit Funktionen nicht so schnell passieren, da du hier ja *explizit* einen Namen vergeben musst. Und wenn du sowieso weißt, dass du "allen Elementen einen Wert X zuweisen" implementiert hast, dann kannst du deine Funktion auch *genau* passend benennen.

    Wie sieht es jetzt aber aus, wenn ich interfaces habe. Wenn ich zB das interface Assignable habe, dann habe ich die Methode assign() die ich jetzt ueberladen muss. Dann ist dies doch sehr aehnlich zu einem op= oder? Denn ich habe ein Symbol dem ich ein Verhalten "zuweisen" muss.

    Ich sehe zwei Unterscheide:
    Dein Assignable-Interface hat a) einen viel kleineren "Scope" und b) eine genau dokumentierte *benutzerdefinierte* Bedeutung. Du hast dein Shade-Framework gebaut und da ein Konzept "Assignable" erdacht. Dieses hast du genau spezifiziert, vielleicht mir Vor- und Nachbedingung. Ich gehe jetzt her und implementiere dein Interface. Da es dein Interface ist, muss ich natürlich in der Dokumentation prüfen, ob meine Implementation den von dir aufgestellten Vertrag erfüllt.

    Ein Operator hingegen hat eine Bedeutung im globalen Kontext die in der Regel eher "traditionell" gewachsen ist, als an einer Stelle fest dokumentiert(manchmal hat ein Operator auch eine Bedeutung die speziell für eine bestimmte Sprache gilt - wie z.B. der operator= in C++. Dann bist du zwar nicht mehr so global wie bei +, aber immer noch nicht im Bereich von "benutzerdefinierte Bedeutung" im Framework X).



  • Wären da jetzt nciht die ganz fiesen Sprachen, die das Freie Benennen von Operatoren gestatten würde wäre das ganze sicherlich eine gute Erklärung :p

    Wenn ich mir jetzt einen Operator ::: definiere, was macht er dann? Jetzt sag nicht, das das unsinn sei. Ich finde diese Möglichkeit ziemlich genial. Wenn es für etwas in der Mathematik keine passende Vorlage gibt, in diesem Framework diese aufgabe aber ständig und immer wieder in der Form vorkommt kann sowas sehr vereinfachend wirken, ähnlich, wie auch ein + zum Addieren das ganze übersichtlicher macht, als eine add-Funktion.



  • Ich finde, dass es durchaus nicht schaden kann, operator overloading einzuschränken.

    Scott Meyers (sinngemäß) schrieb:

    Was möglich ist, wird ein Programmierer irgendwann auch mal tun.

    Sprich: -- zum Trennen einer Verbindung, überladen von || und des Komma-Operators (zum erschießen).
    Den Ansatz von C# finde ich nicht schlecht, weil er doch einige Freiheiten lässt aber gleichzeitig trotzdem z.B. erzwingt, dass a = b einem a = ab entspricht.
    Dass Operatorüberladung in Java ganz fehlt, stört mich manchmal schon auch ein bisschen.

    Wenn ich mir jetzt einen Operator ::: definiere, was macht er dann? Jetzt sag nicht, das das unsinn sei. Ich finde diese Möglichkeit ziemlich genial.

    Ich absolut nicht. 😞 Für solche krassen Sonderwünsche kann man durchaus mal ne Funktion schreiben. Oder soll ich mir vielleicht 'x' für das Kreuzprodukt und . für mein Skalarprodukt überladen? Da wirst du aber staunen, wenn du den Punkt verwenden willst. 😉



  • Ich habe eine Bruch-Klasse.
    Bsp.:
    frac a = (2,3);
    frac b;
    b = frac(2,7) / a * frac(33,7) / sqrt(a);

    Möchte mal wissen, wie man sowas ohne Verrenkungen beim Lesen ohne Overloading hinbekommt.
    Macht kann missbraucht werden, aber deswegen sollte sie nicht abgeschafft werden. Java ist ein prima Beispiel für eine kastrierte Sprache.



  • Helium schrieb:

    Wären da jetzt nciht die ganz fiesen Sprachen, die das Freie Benennen von Operatoren gestatten würde wäre das ganze sicherlich eine gute Erklärung :p

    Ich habe mich in meinen Ausführungen implizit auf C++ und ähnliche Sprachen beschränkt, sprich auf Sprachen, die die Menge der überladbaren Operatoren sowie deren Präzedenzregeln fest definiert (hätte ich vielleicht sagen sollen).

    In dem Moment wo du eigene Operatoren hinzufügen kannst bzw. die Präzedenz eingebauter Operatoren ändern kannst, bist du zweifelsohne in einer ganz anderen Liga mit ganz anderen Regeln. Hier baust du natürlich nicht mehr ausschließlich auf eine "traditionelle" Semantik. Zwar habe ich schon mal mit Sprachen die derartiges bieten (Prolog, Haskel) gearbeitet, wirklich Ahnung habe ich von diesen Sprachen aber nicht. Insofern kann ich dazu nicht viel sinnvolles sagen. Ich denke aber, dass Nutzer dieser Sprachen ganz anders an das Thema rangehen. Wenn ich das richtig sehe sind Operatoren hier ja eigentlich nur Funktionen mit "lustigen" Namen. Es geht hier also wohl nicht so sehr um Polymorphie (neue Implementation für bekannte Operation).



  • Shade Of Mine schrieb:

    Gregor schrieb:

    1. Ja, momentan kann man neben Java nur 189 andere Sprachen nutzen, um Java-Bytecode zu erzeugen: Programming Languages for the Java Virtual Machine

    Und wer macht das?

    Die meisten Sprachen benötigt natürlich keiner. Warum auch: Java ist für die meisten Dinge genausogut oder besser geeignet als andere Sprachen. Allerdings gibt es schon ein paar Sprachen für die JVM, die eine gewisse Relevanz haben. Groovy ist zum Beispiel eine relativ neue Sprache für die JVM, die sehr interessant ist. Ich kann mir auch vorstellen, dass es manchmal sehr gut ist, eine Sprache eines anderen Programmierparadigmas für die JVM zur Verfügung zu haben. Zum Beispiel Prolog und Lisp.

    Für die ganzen Operator-Overloading-Fanatiker hier ist sicherlich ein Pre-Translator wie SubJava oder ein Preprozessor wie JPP interessant, welche einem diese Möglichkeit bieten.



  • Das mit der Lesbarkeit von mathematischen Ausdrücken ist schon ein Argument, aber auch, das man mit einer korrekt benannten Funktion unter Umständen manchmal durchaus besser beraten ist: Bsp.:

    DrawPrimitive drwPrim;//irgendein grafisches Objekt
    POINT ptViewOffset.....
    POINT ptRealCoordOffset.....
    
    drwPrim += ptViewOffset;//was nune??
    
    drwPrim.OffsetRealCoords( ptRealCoordOffset );
    drwPrim.OffsetViewCoords( ptViewOffset );
    

    Für mich, der eher nur Trivial - mathematik anwendet ist das noch kein wirkliches Argument für / oder gegen Java, auch wenn ich rein gefühlsmäßig auch lieber alle Möglichkeiten habe (ich möchte, auch das es schön Kracht, wenn ich mit ungültigen Pointer arbeite :)). this. sieht auch immer noch etwas ungewohnt aus

    Stichwort Präprozessor: Giebt es was vergleichbares in Java / C#?

    [Edit]
    Da hab ich wohl etwas daneben gelegen, es ging ja um C# vs. Java. Da ich beides nur ansatzweise praktisch ausprobiert habe, fällt mir auf, das der Einstieg in C# ein gutes Stück leichter ist. Es ist in C# relativ schnell möglich ein kleines Proggi mit ein paar Zeilen Code zusammen zu bekommen (das sogar irgendwas sinnvolles tut), ohne sich vorher groß damit beschäftigt zu haben.
    Kann aber auch durchaus sein, das ich da als VC Benutzer einfach nur den IDE - Heimvorteil ins Spiel bringe :).



  • Optimizer schrieb:

    Wenn ich mir jetzt einen Operator ::: definiere, was macht er dann? Jetzt sag nicht, das das unsinn sei. Ich finde diese Möglichkeit ziemlich genial.

    Ich absolut nicht. 😞 Für solche krassen Sonderwünsche kann man durchaus mal ne Funktion schreiben. Oder soll ich mir vielleicht 'x' für das Kreuzprodukt und . für mein Skalarprodukt überladen? Da wirst du aber staunen, wenn du den Punkt verwenden willst. 😉

    Nein auf keinen Fall. Das ist es ja. Das sind keine sonderwünsche. Beispielsweise ist das aneinanderhängen zweier Listen in einer funktionalen Sprache genauso essentiell, wie das addieren in der Mathematik. Dementsprechend brauchst du auch eine kurze, übersichtliche Schreibweise.

    @Gregor:
    Danke für die Seite. Auch wenn die meisten Sachen da ciht besonders interessant waren ist JMatch doch verdammt genial und ich werde mich damit mal näher beschäftigen. Java mit Patternmatching wie in funktionalen Sprachen, ... sogar ansatzweise das logische Paradigma. Sieht ziemlich genial aus. 👍

    Wäre Java von sich aus so wäre ich schon längst umgestiegen. Aber JMAtch wird vermutlich weiterhin eine Randgruppensprache bleiben 😞


Anmelden zum Antworten