Überlastet die Methode eine Art Polymorphismus? Für mich scheint es einfach die Unterscheidung von Methoden mit dem gleichen Namen und verschiedenen Parametern zu sein. Also stuff(Thing t)
und stuff(Thing t, int n)
sind ganz andere Methoden, was den Compiler und die Laufzeit angeht.
Auf der Anruferseite entsteht die Illusion, dass dieselbe Methode auf verschiedene Arten von Objekten unterschiedlich wirkt - Polymorphismus. Das ist aber nur eine Illusion, denn eigentlich stuff(Thing t)
und stuff(Thing t, int n)
auch ganz andere Methoden.
Überladen Methoden mehr als syntaktischen Zucker? Vermisse ich etwas?
Eine gebräuchliche Definition für syntaktischen Zucker ist, dass er rein lokal ist . Das heißt, das Ändern eines Codeteils in das gesüßte Äquivalent oder umgekehrt beinhaltet lokale Änderungen, die sich nicht auf die Gesamtstruktur des Programms auswirken. Und ich denke, dass das Überladen von Methoden genau diesem Kriterium entspricht. Schauen wir uns ein Beispiel an, um Folgendes zu demonstrieren:
Betrachten Sie eine Klasse:
class Reader {
public String read(Book b){
// .. translate the book to text
}
public String read(File b){
// .. translate the file to text
}
}
Betrachten Sie nun eine andere Klasse, die diese Klasse verwendet:
/* might not be the best example */
class FileProcessor {
Reader reader = new Reader();
public void process(File file){
String text = reader.read(file);
// .. do stuff with the text
}
}
Okay. Nun wollen wir sehen, was sich ändern muss, wenn wir die Methodenüberladung durch reguläre Methoden ersetzen:
Die read
Methoden im Reader
Wechsel zu readBook(Book)
und readFile(file)
. Nur eine Frage der Namensänderung.
Der aufrufende Code in FileProcessor
ändert sich geringfügig: reader.read(file)
ändert sich zu reader.readFile(file)
.
Und das ist es.
Wie Sie sehen, ist der Unterschied zwischen der Verwendung von Methodenüberladung und deren Nichtverwendung rein lokal . Und deshalb glaube ich, dass es sich um reinen syntaktischen Zucker handelt.
Ich würde gerne Ihre Einwände hören, wenn Sie welche haben, vielleicht fehlt mir etwas.
quelle
Antworten:
Um dies zu beantworten, benötigen Sie zunächst eine Definition für "syntaktischen Zucker". Ich werde mit Wikipedia gehen :
Nach dieser Definition sind Features wie Javas Varargs oder Scalas For-Comprehension syntaktischer Zucker: Sie werden in zugrunde liegende Sprachfeatures übersetzt (ein Array im ersten Fall, Aufrufe von map / flatmap / filter im zweiten Fall) und würden diese entfernen Ändern Sie nicht die Dinge, die Sie mit der Sprache tun können.
Das Überladen von Methoden ist unter dieser Definition jedoch kein syntaktischer Zucker, da das Entfernen die Sprache grundlegend ändern würde (Sie könnten nicht mehr auf ein bestimmtes, auf Argumenten basierendes Verhalten zurückgreifen).
Richtig, Sie können das Überladen von Methoden simulieren, solange Sie eine Möglichkeit haben, auf die Argumente einer Methode zuzugreifen, und Sie können ein "if" -Konstrukt verwenden, das auf den angegebenen Argumenten basiert. Aber wenn Sie diesen syntaktischen Zucker betrachten, müssen Sie alles über einer Turing-Maschine betrachten, um ebenfalls syntaktischer Zucker zu sein.
quelle
sum(numbersArray)
undsum(numbersList)
anstelle vonsumArray(numbersArray)
undsumList(numbersList)
. Ich stimme Doval zu, es scheint nur syntatischer Zucker zu sein.instanceof
, Klassen, Vererbung, Schnittstellen, Generika, Reflexion oder Zugriffsbezeichner mitif
,while
und Booleschen Operatoren, mit der exakt gleichen Semantik . Keine Eckfälle. Beachten Sie, dass ich Sie nicht herausfordere, dieselben Dinge wie die spezifischen Verwendungen dieser Konstrukte zu berechnen. Ich weiß bereits, dass Sie alles mit Boolescher Logik und Verzweigung / Schleife berechnen können. Ich bitte Sie, perfekte Kopien der Semantik dieserDer Begriff syntaktischer Zucker bezieht sich typischerweise auf Fälle, in denen das Merkmal durch eine Substitution definiert ist. Die Sprache definiert nicht, was ein Feature tut, sondern, dass es genau mit etwas anderem äquivalent ist. Also zum Beispiel für jede Schleife
Wird:
Oder nehmen Sie eine Funktion mit variablen Argumenten:
Welches wird:
Es gibt also eine triviale Substitution der Syntax, um das Feature in Bezug auf andere Features zu implementieren.
Schauen wir uns die Methodenüberladung an.
Dies kann wie folgt umgeschrieben werden:
Aber das ist nicht gleichbedeutend. Innerhalb des Java-Modells ist dies etwas anderes.
foo(int a)
implementiert keinefoo_int
zu erstellende Funktion. Java implementiert keine Methodenüberladung, indem es mehrdeutigen Funktionen lustige Namen gibt. Um als syntaktischer Zucker zu gelten, müsste Java so tun, als ob Sie wirklich geschrieben hättenfoo_int
undfoo_double
funktionieren, aber das tut es nicht.quelle
But, the transformation isn't trivial. At the least, you have to determine the types of the parameters.
ja , finde ich die Behauptung sehr lückenhaft, da die Typen nicht bestimmt werden müssen ; Sie sind zur Kompilierungszeit bekannt.foo(int)
/foo(double)
undfoo_int
/, solange es sich um Überladungsmethoden und nicht um Polymorphismen handeltfoo_double
? Ich kenne Java nicht wirklich gut, aber ich würde mir vorstellen, dass eine solche Umbenennung wirklich in JVM stattfindet (naja - wahrscheinlichfoo(args)
eher dannfoo_args
- zumindest in C ++ mit Symbol-Mangling (ok - Symbol-Mangling ist technisch ein Implementierungsdetail und kein Teil davon der Sprache)for
Schleife nicht aussagekräftiger als Java ohne. (Es ist schöner, prägnanter, lesbarer und rundum besser, würde ich argumentieren, aber nicht aussagekräftiger.) Ich bin mir jedoch nicht sicher, was den Überlastungsfall angeht. Ich werde wahrscheinlich die Zeitung noch einmal lesen müssen, um sicherzugehen. Mein Darm sagt, es ist syntaktischer Zucker, aber ich bin nicht sicher.Muss es angesichts der Tatsache, dass die Namensverfälschung funktioniert, nicht mehr als syntaktischer Zucker sein?
Der Anrufer kann sich vorstellen, dass er dieselbe Funktion aufruft, wenn er es nicht ist. Aber er konnte die wahren Namen aller seiner Funktionen kennen. Nur wenn es möglich wäre, durch Übergabe einer untypisierten Variablen an eine typisierte Funktion einen verzögerten Polymorphismus zu erzielen und den Typ so festzulegen, dass der Aufruf entsprechend dem Namen an die richtige Version geleitet werden kann, wäre dies ein echtes Sprachmerkmal.
Leider habe ich noch nie eine Sprache gesehen, die dies tut. Wenn es Unklarheiten gibt, lösen diese Compiler diese nicht auf. Sie bestehen darauf, dass der Schreiber sie für sie auflöst.
quelle
dynamic
dann tritt die Überladungsauflösung zur Laufzeit nicht zur Übersetzungszeit . Das ist Multiple Dispatch, und es kann nicht durch Umbenennen von Funktionen repliziert werden.Je nach Sprache ist es syntaktischer Zucker oder nicht.
In C ++ können Sie beispielsweise Überladungen und Vorlagen verwenden, die ohne Komplikationen nicht möglich wären (schreiben Sie alle Instanziierungen der Vorlage manuell oder fügen Sie viele Vorlagenparameter hinzu).
Beachten Sie, dass dynamischer Dispatch eine Form der Überlastung ist, dynamisch auf einigen Parametern aufgelöst (für einige Sprachen nur ein besonderer, diese , aber nicht alle Sprachen sind begrenzt , so), und ich würde diese Form der Überlastung syntaktischen Zuckers nicht nennen.
quelle
Für heutige Sprachen ist es nur syntaktischer Zucker; In einer völlig sprachunabhängigen Art und Weise ist es mehr als das.
Früher hat diese Antwort einfach gesagt, dass es mehr als syntaktischer Zucker ist, aber wenn Sie in den Kommentaren sehen, hat Falco den Punkt angesprochen, dass es ein Puzzlestück gibt, das allen modernen Sprachen fehlt. Sie kombinieren Methodenüberladung nicht mit der dynamischen Bestimmung, welche Funktion im selben Schritt aufgerufen werden soll. Dies wird später noch geklärt.
Hier ist , warum es sollte mehr sein.
Stellen Sie sich eine Sprache vor, die sowohl Methodenüberladung als auch untypisierte Variablen unterstützt. Sie könnten die folgenden Methodenprototypen haben:
In einigen Sprachen sind Sie wahrscheinlich damit abgefunden, beim Kompilieren zu wissen, welche davon von einer bestimmten Codezeile aufgerufen wird. In einigen Sprachen werden jedoch nicht alle Variablen eingegeben (oder sie werden alle implizit als
Object
oder wie auch immer eingegeben ). Stellen Sie sich daher vor, Sie erstellen ein Wörterbuch, dessen Schlüssel Werten verschiedener Typen zugeordnet sind:Was wäre, wenn Sie sich für
someFunction
eine dieser Zimmernummern bewerben wollten ? Sie nennen das:Heißt
someFunction(int)
odersomeFunction(string)
heißt? Hier sehen Sie ein Beispiel, bei dem es sich nicht um vollständig orthogonale Methoden handelt, insbesondere in höheren Sprachen. Die Sprache muss - während der Laufzeit - herausfinden, welche davon aufgerufen werden sollen, und muss diese daher immer noch als mindestens einigermaßen dieselbe Methode betrachten.Warum nicht einfach Vorlagen verwenden? Warum nicht einfach ein untypisiertes Argument verwenden?
Flexibilität und feinkörnigere Kontrolle. Manchmal ist die Verwendung von Vorlagen / untypisierten Argumenten besser, manchmal jedoch nicht.
Sie müssen sich Fälle überlegen, in denen Sie beispielsweise zwei Methodensignaturen haben, die jeweils ein
int
und einstring
als Argument verwenden, bei denen die Reihenfolge in jeder Signatur jedoch unterschiedlich ist. Möglicherweise haben Sie einen guten Grund, dies zu tun, da die Implementierung jeder Signatur im Großen und Ganzen dasselbe tut, jedoch mit einer geringfügig anderen Wendung. Die Protokollierung kann beispielsweise unterschiedlich sein. Selbst wenn sie genau dasselbe tun, können Sie möglicherweise bestimmte Informationen automatisch aus der Reihenfolge abrufen, in der die Argumente angegeben wurden. Technisch könnte man einfach Pseudo-Switch-Anweisungen verwenden, um den Typ jedes der übergebenen Argumente zu bestimmen, aber das wird chaotisch.Ist das nächste Beispiel also eine schlechte Programmierpraxis?
Ja, im Großen und Ganzen. In diesem speziellen Beispiel könnte es jemanden davon abhalten, dies auf bestimmte primitive Typen anzuwenden und unerwartetes Verhalten zurückzugewinnen (was eine gute Sache sein könnte); aber nehmen wir einfach an, dass ich den obigen Code abgekürzt habe und dass Sie tatsächlich Überladungen für alle primitiven Typen sowie für
Object
s haben. Dann ist dieses nächste Stück Code wirklich angemessener:Aber was ist, wenn Sie dies nur für
int
s undstring
s benötigen und wenn Sie möchten, dass es unter einfacheren oder komplizierteren Bedingungen als wahr zurückgegeben wird? Dann haben Sie einen guten Grund, Überladung zu verwenden:Aber warum geben Sie diesen Funktionen nicht einfach zwei unterschiedliche Namen? Sie haben immer noch die gleiche Kontrolle, nicht wahr?
Wie bereits erwähnt, verwenden einige Hotels Zahlen, einige Buchstaben und einige eine Mischung aus Zahlen und Buchstaben:
Dies ist immer noch nicht genau derselbe genaue Code, den ich im wirklichen Leben verwenden würde, aber er sollte den Punkt veranschaulichen, den ich gerade gut mache.
Aber ... Deshalb ist es in modernen Sprachen nicht mehr als syntaktischer Zucker.
Falco wies in den Kommentaren darauf hin, dass derzeitige Sprachen Methodenüberladung und dynamische Funktionsauswahl grundsätzlich nicht im selben Schritt mischen. Früher verstand ich bestimmte Sprachen so, dass Sie sie
appearsToBeFirstFloor
im obigen Beispiel überladen konnten. Die Sprache bestimmte dann zur Laufzeit, welche Version der Funktion aufgerufen werden soll, abhängig vom Laufzeitwert der untypisierten Variablen. Diese Verwirrung ergab sich teilweise aus der Arbeit mit ECMA-Sprachen wie ActionScript 3.0, in denen Sie leicht nach dem Zufallsprinzip bestimmen können, welche Funktion zur Laufzeit in einer bestimmten Codezeile aufgerufen wird.Wie Sie vielleicht wissen, unterstützt ActionScript 3 das Überladen von Methoden nicht. Wie bei VB.NET können Sie Variablen deklarieren und festlegen, ohne explizit einen Typ zuzuweisen. Wenn Sie jedoch versuchen, diese Variablen als Argumente an überladene Methoden zu übergeben, möchten Sie den Laufzeitwert nicht lesen, um zu bestimmen, welche Methode aufgerufen werden soll. es möchte stattdessen eine Methode mit Argumenten vom Typ
Object
oder ohne Typ oder so etwas finden. Das obigeint
vs.string
Beispiel würde also auch in dieser Sprache nicht funktionieren. C ++ hat ähnliche Probleme, wie wenn Sie so etwas wie einen Void-Zeiger oder einen anderen Mechanismus wie diesen verwenden, müssen Sie den Typ beim Kompilieren immer noch manuell disambiguieren.Also wie der erste Header sagt ...
Für heutige Sprachen ist es nur syntaktischer Zucker; In einer völlig sprachunabhängigen Art und Weise ist es mehr als das. Das Überladen von Methoden nützlicher und relevanter zu machen, wie im obigen Beispiel, kann tatsächlich eine gute Funktion sein, um eine vorhandene Sprache zu ergänzen (wie dies für AS3 allgemein implizit gefordert wurde), oder es könnte auch als eine von vielen verschiedenen Grundpfeilern für dienen die Schaffung einer neuen prozeduralen / objektorientierten Sprache.
quelle
this[chooseFunctionNameAtRandom]();
WennchooseFunctionNameAtRandom()
Rückkehr entweder"punch"
,"kick"
oder"dodge"
, dann kann man auf diese Weise einen sehr einfachen Zufall implementieren Ein Element in der KI eines Feindes in einem Flash-Spiel.Es hängt wirklich von Ihrer Definition des "syntaktischen Zuckers" ab. Ich werde versuchen, einige der Definitionen anzusprechen, die mir einfallen:
Ein Feature ist syntaktischer Zucker, wenn ein Programm, das es verwendet, immer in ein anderes übersetzt werden kann, das das Feature nicht verwendet.
Hier nehmen wir an, dass es einen primitiven Satz von Merkmalen gibt, die nicht übersetzt werden können: mit anderen Worten, keine Schleifen der Art "Sie können Merkmal X durch Merkmal Y ersetzen" und "Sie können Merkmal Y durch Merkmal X ersetzen". Wenn eines der beiden Merkmale zutrifft, kann das andere Merkmal in Merkmalen ausgedrückt werden, die nicht das erste sind, oder es ist ein primitives Merkmal.
Wie Definition 1, jedoch mit der zusätzlichen Anforderung, dass das übersetzte Programm genauso typsicher ist wie das erste, dh durch Desugaring gehen keinerlei Informationen verloren.
Die Definition des OP: Ein Feature ist syntaktischer Zucker, wenn seine Übersetzung die Struktur des Programms nicht verändert, sondern nur "lokale Änderungen" erfordert.
Nehmen wir Haskell als Beispiel für Überladung. Haskell bietet eine benutzerdefinierte Überladung über Typklassen. Zum Beispiel sind die
+
und*
-Operationen in derNum
Typklasse definiert und jeder Typ, der eine (vollständige) Instanz einer solchen Klasse hat, kann mit verwendet werden+
. Beispielsweise:Eine bekannte Sache bei Haskells Typenklassen ist, dass man sie loswerden kann . Dh Sie können jedes Programm, das Typklassen verwendet, in ein gleichwertiges Programm übersetzen, das diese nicht verwendet.
Die Übersetzung ist ganz einfach:
Eine Klassendefinition gegeben:
Sie können es in einen algebraischen Datentyp übersetzen:
Hier
X_P_i
undX_op_i
sind Selektoren . Wenn also ein Wert vom Typ auf den WertX a
angewendet wirdX_P_1
, wird der in diesem Feld gespeicherte Wert zurückgegeben, sodass es sich um Funktionen mit dem TypX a -> P_i a
(oderX a -> t_i
) handelt.Für eine sehr grobe Anologie könnte man sich die Werte für type
X a
alsstruct
s vorstellen und dann, wennx
es sich um einen Typ handelt,X a
die Ausdrücke:könnte gesehen werden als:
(Es ist einfach, nur Positionsfelder anstelle von benannten Feldern zu verwenden, aber benannte Felder sind in den Beispielen einfacher zu handhaben und vermeiden Kesselplattencode.)
Eine Instanzdeklaration gegeben:
Sie können es in eine Funktion übersetzen, bei der die Wörterbücher für die
C_1 a_1, ..., C_n a_n
Klassen einen Wörterbuchwert (dh einen Wert vom TypX a
) für den Typ zurückgebenT a_1 ... a_n
.Mit anderen Worten kann die obige Instanz in eine Funktion übersetzt werden wie:
(Beachten Sie, dass sein
n
kann0
).Und in der Tat können wir es definieren als:
wo
op_1 = ...
aufop_m = ...
die in den Definitioneninstance
Erklärung und dasget_P_i_T
sind die durch die definierten FunktionenP_i
Instanz desT
Typs (Diese müssen vorhanden sein, daP_i
s sind Superklassen vonX
).Aufruf einer überladenen Funktion gegeben:
Wir können die Wörterbücher explizit in Bezug auf die Klasseneinschränkungen übergeben und einen entsprechenden Aufruf erhalten:
Beachten Sie, wie die Klasseneinschränkungen einfach zu einem neuen Argument wurden. Das
+
im übersetzten Programm ist der Selektor wie zuvor erklärt. Mit anderen Worten, die übersetzteadd
Funktion "entpackt" bei gegebenem Wörterbuch für den Typ ihres Arguments zuerst die eigentliche Funktion, um das Ergebnis mit zu berechnen,(+) dictNum
und wendet diese Funktion dann auf die Argumente an.Dies ist nur eine sehr kurze Skizze über das Ganze. Bei Interesse lesen Sie bitte die Artikel von Simon Peyton Jones et al.
Ich glaube, ein ähnlicher Ansatz könnte auch für Überladungen in anderen Sprachen verwendet werden.
Dies zeigt jedoch, dass, wenn Ihre Definition von syntaktischem Zucker (1) ist, Überladung syntaktischer Zucker ist . Weil du es loswerden kannst.
Das übersetzte Programm verliert jedoch einige Informationen über das ursprüngliche Programm. Beispielsweise wird nicht erzwungen, dass die Instanzen für die übergeordneten Klassen vorhanden sind. (Auch wenn die Operationen zum Extrahieren der Wörterbücher des übergeordneten Elements immer noch von diesem Typ sein müssen, können Sie
undefined
Werte übergeben oder andere polymorphe Werte eingeben, damit Sie einen Wert für erstellen können,X y
ohne die Werte für zu erstellenP_i y
, sodass die Übersetzung nicht alle verliert die Typensicherheit). Daher ist es kein syntaktischer Zucker gemäß (2)Wie für (3). Ich weiß nicht, ob die Antwort ein Ja oder ein Nein sein soll.
Ich würde nein sagen, weil beispielsweise eine Instanzdeklaration zu einer Funktionsdefinition wird. Überladene Funktionen erhalten einen neuen Parameter (dh es werden sowohl die Definition als auch alle Aufrufe geändert).
Ich würde ja sagen, weil die beiden Programme immer noch eins zu eins abbilden, so dass die "Struktur" nicht wirklich so stark verändert wird.
Trotzdem würde ich sagen, dass die pragmatischen Vorteile des Überladens so groß sind, dass die Verwendung eines "abfälligen" Begriffs wie "syntaktischer Zucker" nicht richtig erscheint.
Sie können die gesamte Haskell-Syntax in eine sehr einfache Core-Sprache übersetzen (die beim Kompilieren tatsächlich ausgeführt wird), sodass der größte Teil der Haskell-Syntax als "syntaktischer Zucker" für etwas angesehen werden kann, das nur aus Lambda-Kalkül und ein bisschen neuen Konstrukten besteht. Wir können jedoch zustimmen, dass die Haskell-Programme viel einfacher zu handhaben und sehr präzise sind, wohingegen die übersetzten Programme schwerer zu lesen oder zu überlegen sind.
quelle
Wenn der Versand zur Kompilierungszeit aufgelöst wird, was nur vom statischen Typ des Argumentausdrucks abhängt, können Sie mit Sicherheit argumentieren, dass es sich um "syntaktischen Zucker" handelt, der zwei verschiedene Methoden mit unterschiedlichen Namen ersetzt, vorausgesetzt, der Programmierer "kennt" den statischen Typ und könnte einfach den richtigen Methodennamen anstelle des überladenen Namens verwenden. Es ist auch eine Form des statischen Polymorphismus, aber in dieser begrenzten Form ist es normalerweise nicht sehr mächtig.
Natürlich wäre es lästig, die Namen der von Ihnen aufgerufenen Methoden zu ändern, wenn Sie den Typ einer Variablen ändern. In der Sprache C wird dies jedoch als überschaubar angesehen, sodass C keine Funktionsüberladung aufweist (obwohl es hat jetzt generische Makros).
In C ++ - Vorlagen und in allen Sprachen, in denen nicht-triviale Ableitungen statischer Typen verwendet werden, können Sie nicht wirklich behaupten, dass dies "syntaktischer Zucker" ist, es sei denn, Sie argumentieren auch, dass Ableitungen statischer Typen "syntaktischer Zucker" sind. Es wäre ein Ärgernis, keine Vorlagen zu haben, und im Kontext von C ++ wäre es ein "unüberschaubares Ärgernis", da sie für die Sprache und ihre Standardbibliotheken so idiomatisch sind. In C ++ ist es also eher mehr als ein netter Helfer, es ist wichtig für den Stil der Sprache, und ich denke, man muss es mehr nennen als "syntaktischer Zucker".
In Java ist dies möglicherweise mehr als nur eine Annehmlichkeit, wenn man bedenkt, wie viele Überladungen von
PrintStream.print
und vorliegenPrintStream.println
. Es gibt jedoch ebenso vieleDataInputStream.readX
Methoden, da Java beim Rückgabetyp nicht überlastet, was in gewisser Weise nur der Einfachheit halber dient. Das sind alles für primitive Typen.Ich erinnere mich nicht , was in Java passiert , wenn ich Klassen
A
undB
erstreckenO
, ich überlastete Methodenfoo(O)
,foo(A)
undfoo(B)
, und dann in einem allgemeinen mit<T extends O>
nenne ichfoo(t)
wot
eine InstanzT
. In dem Fall , woT
istA
erhalte ich Versand auf der Grundlage der Überlast oder ist es , als ob ich genanntfoo(O)
?In diesem Fall sind Java-Methodenüberladungen genauso besser als Zucker wie C ++ - Überladungen. Unter Verwendung Ihrer Definition könnte ich in Java lokal eine Reihe von Typprüfungen schreiben (was zerbrechlich wäre, da neue Überladungen
foo
zusätzliche Prüfungen erfordern würden). Abgesehen davon, dass ich diese Zerbrechlichkeit akzeptiere, kann ich an der Call-Site keine lokalen Änderungen vornehmen , um sie zu korrigieren. Stattdessen müsste ich auf das Schreiben von generischem Code verzichten. Ich würde argumentieren, dass das Verhindern von aufgeblähtem Code syntaktischer Zucker sein könnte, aber das Verhindern von fragilem Code ist mehr als das. Aus diesem Grund ist statischer Polymorphismus im Allgemeinen mehr als nur syntaktischer Zucker. Die Situation in einer bestimmten Sprache kann unterschiedlich sein, je nachdem, wie weit die Sprache es Ihnen erlaubt, den statischen Typ "nicht zu kennen".quelle
T:Animal
ist , ArtSiameseCat
und bestehende Überlastungen sindCat Foo(Animal)
,SiameseCat Foo(Cat)
undAnimal Foo(SiameseCat)
, die Überlastung sollte , wenn ausgewählt werdenT
istSiameseCat
?long foo=Math.round(bar*1.0001)*5
wird in geändertlong foo=Math.round(bar)*5
. Wie würde sich das auf die Semantik auswirken, wennbar
z. B. 123456789L gleich ist?long
nachdouble
.double
?Es sieht so aus, als ob "syntaktischer Zucker" abwertend klingt, als ob er nutzlos oder leichtfertig ist. Deshalb löst die Frage viele negative Antworten aus.
Aber Sie haben Recht, das Überladen von Methoden fügt der Sprache keine Funktion hinzu, außer dass derselbe Name für verschiedene Methoden verwendet werden kann. Sie können den Parametertyp explizit angeben, das Programm funktioniert trotzdem.
Gleiches gilt für Paketnamen. String ist nur syntaktischer Zucker für java.lang.String.
In der Tat eine Methode wie
in der Klasse sollte MyClass so etwas wie "my_package_MyClass_fun_int_java_lang_String" heißen. Dies würde die Methode eindeutig identifizieren. (Die JVM macht so etwas intern). Aber das willst du nicht schreiben. Aus diesem Grund lässt der Compiler Sie Spaß schreiben (1, "eins") und identifizieren, welche Methode es ist.
Mit dem Überladen können Sie jedoch eines tun: Wenn Sie eine Methode mit der gleichen Anzahl von Argumenten überladen, ermittelt der Compiler automatisch, welche Version am besten zu dem Argument passt, das durch übereinstimmende Argumente angegeben wird Das angegebene Argument ist eine Unterklasse des deklarierten Arguments.
Wenn Sie zwei überladene Prozeduren haben
Sie müssen nicht wissen, dass es für Dates eine bestimmte Version des Verfahrens gibt. addParameter ("hello", "world") ruft die erste Version auf, addParameter ("now", new Date ()) ruft die zweite auf.
Natürlich sollten Sie vermeiden, eine Methode mit einer anderen Methode zu überladen, die etwas völlig anderes tut.
quelle
Interessanterweise hängt die Antwort auf diese Frage von der Sprache ab.
Insbesondere besteht eine Wechselwirkung zwischen Überladung und generischer Programmierung (*), und je nachdem, wie generische Programmierung implementiert ist, kann es sich lediglich um syntaktischen Zucker (Rust) oder um absolut notwendige (C ++) handeln.
Das heißt, wenn generische Programmierung mit expliziten Schnittstellen implementiert wird (in Rust oder Haskell wären dies Typklassen), ist Überladung nur syntaktischer Zucker. oder vielleicht sogar nicht Teil der Sprache.
Wenn die generische Programmierung hingegen mit Duck-Typing implementiert wird (sei es dynamisch oder statisch), ist der Name der Methode ein wesentlicher Vertrag, und daher ist eine Überladung für die Funktionsfähigkeit des Systems obligatorisch.
(*) Wird im Sinne eines einmaligen Schreibens einer Methode verwendet, um verschiedene Typen einheitlich zu bearbeiten.
quelle
In einigen Sprachen handelt es sich zweifellos nur um syntaktischen Zucker. Was ein Zucker ist, hängt jedoch von Ihrer Sichtweise ab. Ich werde diese Diskussion für später in dieser Antwort belassen.
Im Moment möchte ich nur erwähnen, dass es sich in einigen Sprachen sicherlich nicht um syntaktischen Zucker handelt. Zumindest nicht, ohne dass Sie eine völlig andere Logik / einen anderen Algorithmus verwenden müssen, um dasselbe zu implementieren. Es ist, als würde man behaupten, Rekursion sei syntaktischer Zucker (was der Fall ist, da man alle rekursiven Algorithmen mit einer Schleife und einem Stapel schreiben kann).
Ein Beispiel für eine sehr schwer zu ersetzende Verwendung stammt aus einer Sprache, die diese Funktion ironischerweise nicht als "Funktionsüberladung" bezeichnet. Stattdessen wird es "Mustervergleich" genannt (was als Übermenge der Überladung angesehen werden kann, da wir nicht nur Typen, sondern auch Werte überladen können).
Hier ist die klassisch naive Implementierung der Fibonacci-Funktion in Haskell:
Es ist anzunehmen, dass die drei Funktionen durch ein if / else ersetzt werden können, wie es üblicherweise in jeder anderen Sprache durchgeführt wird. Aber das macht die Definition im Grunde ganz einfach:
viel chaotischer und drückt den mathematischen Begriff der Fibonacci-Folge nicht direkt aus.
Manchmal kann es sich also um Syntaxzucker handeln, wenn Sie nur eine Funktion mit unterschiedlichen Argumenten aufrufen können. Aber manchmal ist es viel grundlegender.
Nun zur Diskussion, wofür das Überladen von Operatoren ein Zucker sein kann. Sie haben einen Anwendungsfall identifiziert - er kann verwendet werden, um ähnliche Funktionen mit unterschiedlichen Argumenten zu implementieren. So:
kann alternativ implementiert werden als:
oder auch:
Das Überladen von Operatoren kann aber auch ein Zucker für die Implementierung optionaler Argumente sein (einige Sprachen haben das Überladen von Operatoren, aber keine optionalen Argumente):
kann verwendet werden, um Folgendes zu implementieren:
In einer solchen Sprache (google "Ferite language") wird durch das Entfernen der Operatorüberladung eine Funktion drastisch entfernt - optionale Argumente. Gewährt in Sprachen, in denen beide Funktionen (c ++) das eine oder das andere entfernen, hat dies keine Auswirkungen, da beide zur Implementierung optionaler Argumente verwendet werden können.
quelle
data PaymentInfo = CashOnDelivery | Adress String | UserInvoice CustomerInfo
, können Sie die Muster für die Typkonstruktoren abgleichen.getPayment :: PaymentInfo -> a
getPayment CashOnDelivery = error "Should have been paid already"
getPayment (Adress addr) = -- code to notify administration to send a bill
getPayment (UserInvoice cust) = --whatever. I took the data type from a Haskell tutorial and have no idea what an invoice is
. Ich hoffe dieser Kommentar ist etwas verständlich.Ich denke, es ist einfacher syntaktischer Zucker in den meisten Sprachen (zumindest weiß ich ...), da sie alle einen eindeutigen Funktionsaufruf zur Kompilierungszeit erfordern. Und der Compiler ersetzt den Funktionsaufruf einfach durch einen expliziten Zeiger auf die richtige Implementierungssignatur.
Beispiel in Java:
Am Ende könnte es also komplett durch ein einfaches Compiler-Makro mit Suchen und Ersetzen ersetzt werden, das die überladene Funktion mangle durch mangle_String und mangle_int ersetzt - da die Argumentliste Teil der eventuellen Funktionskennung ist, ist dies praktisch das, was passiert -> und deshalb ist es nur syntaktischer Zucker.
Wenn es nun eine Sprache gibt, in der die Funktion zur Laufzeit wirklich festgelegt ist, wie bei überschriebenen Methoden in Objekten, wäre dies anders. Aber ich glaube nicht, dass es eine solche Sprache gibt, da method.overloading zu Mehrdeutigkeiten neigt, die der Compiler nicht auflösen kann und die vom Programmierer mit einer expliziten Umwandlung behandelt werden müssen. Dies ist zur Laufzeit nicht möglich.
quelle
In Java werden Typinformationen kompiliert und es wird beim Kompilieren entschieden, welche der Überladungen aufgerufen wird.
Das Folgende ist ein Ausschnitt aus
sun.misc.Unsafe
(dem Dienstprogramm für Atomics), der im Klassendatei-Editor von Eclipse angezeigt wird.Wie Sie sehen können, sind Typinformationen der aufgerufenen Methode (Zeile 4) im Aufruf enthalten.
Dies bedeutet, dass Sie einen Java-Compiler erstellen können, der Typinformationen enthält. Wenn Sie beispielsweise eine solche Notation verwenden, lautet die Quelle wie folgt:
und die Besetzung zu lang wäre optional.
In anderen statisch typisierten kompilierten Sprachen sehen Sie ein ähnliches Setup, bei dem der Compiler entscheidet, welche Überladung je nach Typ aufgerufen wird, und sie in die Bindung / den Aufruf einbezieht.
Die Ausnahme bilden dynamische C-Bibliotheken, in denen die Typinformationen nicht enthalten sind, und der Versuch, eine überladene Funktion zu erstellen, führt zu einer Beschwerde des Linkers.
quelle