La surcharge de méthode est-elle un type de polymorphisme? Cela me semble être simplement la différenciation de méthodes portant le même nom et des paramètres différents. Ainsi , stuff(Thing t)
et stuff(Thing t, int n)
sont des méthodes totalement différentes dans la mesure où le compilateur et l' exécution sont concernés.
Cela crée l'illusion, du côté de l'appelant, que c'est la même méthode qui agit différemment sur différents types d'objets - le polymorphisme. Mais ce n'est qu'une illusion, car en fait stuff(Thing t)
et ce stuff(Thing t, int n)
sont des méthodes complètement différentes.
La méthode est-elle en train de surcharger autre chose que du sucre syntaxique? Suis-je en train de manquer quelque chose?
Une définition courante du sucre syntaxique est qu'il est purement local . Cela signifie que changer un morceau de code en son équivalent «sucré», ou vice versa, implique des changements locaux qui n'affectent pas la structure globale du programme. Et je pense que la surcharge de méthode correspond précisément à ce critère. Regardons un exemple pour démontrer:
Considérez une classe:
class Reader {
public String read(Book b){
// .. translate the book to text
}
public String read(File b){
// .. translate the file to text
}
}
Considérons maintenant une autre classe qui utilise cette classe:
/* 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
}
}
D'accord. Voyons maintenant ce qui doit changer si nous remplaçons la surcharge de méthode par des méthodes régulières:
Les read
méthodes de Reader
changement vers readBook(Book)
et readFile(file)
. Il suffit de changer leurs noms.
Le code appelant FileProcessor
change légèrement: reader.read(file)
devient reader.readFile(file)
.
Et c'est tout.
Comme vous pouvez le voir, la différence entre utiliser la surcharge de méthode et ne pas l'utiliser est purement locale . Et c'est pourquoi je pense qu'il peut être considéré comme du sucre syntaxique pur.
J'aimerais entendre vos objections si vous en avez, peut-être que je manque quelque chose.
la source
Réponses:
Pour répondre à cela, vous avez d'abord besoin d'une définition du «sucre syntaxique». J'irai avec Wikipédia :
Ainsi, selon cette définition, des fonctionnalités telles que les varargs de Java ou la compréhension de Scala sont du sucre syntaxique: elles se traduisent en fonctionnalités de langage sous-jacentes (un tableau dans le premier cas, des appels à map / flatmap / filter dans le second), et les supprimer serait pas changer les choses que vous pouvez faire avec la langue.
La surcharge de méthode, cependant, n'est pas du sucre syntaxique dans cette définition, car sa suppression changerait fondamentalement la langue (vous ne seriez plus en mesure de répartir vers un comportement distinct basé sur des arguments).
Certes, vous pouvez simuler une surcharge de méthode tant que vous disposez d'un moyen d'accéder aux arguments d'une méthode et pouvez utiliser une construction "if" basée sur les arguments qui vous sont fournis. Mais si vous considérez ce sucre syntaxique, vous devriez considérer que tout ce qui se trouve au-dessus d'une machine de Turing est également du sucre syntaxique.
la source
sum(numbersArray)
etsum(numbersList)
au lieu desumArray(numbersArray)
etsumList(numbersList)
. Je suis d'accord avec Doval, cela ressemble à du simple sucre syntaxique.instanceof
, les classes, l' héritage, les interfaces, les génériques, la réflexion ou spécificateurs d'accès à l' aideif
,while
et les opérateurs booléens, avec exactement le même sémantique . Pas de cas d'angle. Notez que je ne vous mets pas au défi de calculer les mêmes choses que les utilisations spécifiques de ces constructions. Je sais déjà que vous pouvez calculer n'importe quoi en utilisant la logique booléenne et la ramification / boucle. Je vous demande d'implémenter des copies parfaites de la sémantique de ces fonctionnalités de langage, y compris toutes les garanties statiques qu'elles fournissent (les vérifications de temps de compilation doivent encore être effectuées au moment de la compilation.)Le terme sucre syntaxique se réfère généralement aux cas où la caractéristique est définie par une substitution. Le langage ne définit pas ce que fait une fonctionnalité, mais définit plutôt qu'elle est exactement équivalente à autre chose. Ainsi, par exemple, pour chaque boucle
Devient:
Ou prenez une fonction avec des arguments variables:
Ce qui devient:
Il y a donc une substitution triviale de syntaxe pour implémenter la fonctionnalité en termes d'autres fonctionnalités.
Examinons la surcharge de méthode.
Cela peut être réécrit comme:
Mais ce n'est pas équivalent à cela. Dans le modèle de Java, c'est quelque chose de différent.
foo(int a)
n'implémente pas defoo_int
fonction à créer. Java n'implémente pas la surcharge de méthode en donnant des noms amusants aux fonctions ambiguës. Pour compter comme sucre syntaxique, java devrait prétendre que vous avez vraiment écritfoo_int
etfoo_double
fonctionne, mais ce n'est pas le cas.la source
But, the transformation isn't trivial. At the least, you have to determine the types of the parameters.
très sommaire car les types n'ont pas besoin d'être déterminés ; ils sont connus au moment de la compilation.foo(int)
/foo(double)
etfoo_int
/foo_double
? Je ne connais pas vraiment très bien Java mais j'imagine qu'un tel changement de nom se produit vraiment dans JVM (enfin - probablement en utilisantfoo(args)
plutôt alorsfoo_args
- il le fait au moins en C ++ avec la manipulation de symboles (ok - la manipulation de symboles est techniquement un détail d'implémentation et ne fait pas partie) de la langue).for
boucle améliorée n'est pas plus expressif que Java sans lui. (Je dirais que c'est plus agréable, plus concis, plus lisible, et mieux dans l'ensemble, mais pas plus expressif.) Je ne suis pas sûr du cas de surcharge, cependant. Je vais probablement devoir relire le papier pour être sûr. Mon instinct dit qu'il est le sucre syntaxique, mais je ne suis pas sûr.Étant donné que la manipulation des noms fonctionne, ne doit-il pas être rien de plus que du sucre syntaxique?
Il permet à l'appelant d'imaginer qu'il appelle la même fonction, alors qu'il ne l'est pas. Mais il pouvait connaître les vrais noms de toutes ses fonctions. Ce n'est que s'il était possible d'obtenir un polymorphisme retardé en passant une variable non typée dans une fonction typée et que son type soit établi de sorte que l'appel puisse passer à la bonne version en fonction de son nom que ce serait une véritable fonctionnalité de langage.
Malheureusement, je n'ai jamais vu une langue faire ça. Lorsqu'il y a ambiguïté, ces compilateurs ne le résolvent pas, ils insistent pour que le rédacteur le résolve pour eux.
la source
dynamic
alors la résolution de surcharge se produit au moment de l'exécution, pas au moment de la compilation . C'est ce qu'est la répartition multiple, et elle ne peut pas être reproduite en renommant les fonctions.Selon la langue, c'est du sucre syntaxique ou non.
En C ++ par exemple, vous pouvez faire des choses en utilisant une surcharge et des modèles qui ne seraient pas possibles sans complications (écrivez manuellement toutes les instanciations du modèle ou ajoutez beaucoup de paramètres de modèle).
Notez que la répartition dynamique est une forme de surcharge, résolue dynamiquement sur certains paramètres (pour certaines langues seulement une spéciale, cela , mais pas toutes les langues sont si limitées), et je n'appellerais pas cette forme de surcharge syntaxique de sucre.
la source
Pour les langues contemporaines, c'est juste du sucre syntaxique; d'une manière complètement indépendante de la langue, c'est plus que cela.
Auparavant, cette réponse indiquait simplement que c'était plus que du sucre syntaxique, mais si vous voyez dans les commentaires, Falco a soulevé le fait qu'il y avait une pièce du puzzle que les langues contemporaines semblaient toutes manquer; ils ne mélangent pas la surcharge de méthode avec la détermination dynamique de la fonction à appeler dans la même étape. Cela sera clarifié plus tard.
Voici pourquoi cela devrait être plus.
Prenons un langage qui prend en charge la surcharge de méthode et les variables non typées. Vous pouvez avoir les prototypes de méthodes suivants:
Dans certaines langues, vous seriez probablement résigné à savoir au moment de la compilation lequel de ceux-ci serait appelé par une ligne de code donnée. Mais dans certaines langues, toutes les variables ne sont pas typées (ou elles sont toutes implicitement typées comme
Object
ou autre), alors imaginez construire un dictionnaire dont les clés correspondent à des valeurs de différents types:Maintenant, que se passe-t-il si vous souhaitez postuler
someFunction
à l'un de ces numéros de chambre? Vous appelez cela:Est
someFunction(int)
appelé, ou estsomeFunction(string)
appelé? Ici, vous voyez un exemple où ce ne sont pas des méthodes totalement orthogonales, en particulier dans les langues de niveau supérieur. Le langage doit déterminer - lors de l'exécution - lequel de ces éléments appeler, il doit donc toujours les considérer comme étant au moins un peu la même méthode.Pourquoi ne pas simplement utiliser des modèles? Pourquoi ne pas simplement utiliser un argument non typé?
Flexibilité et contrôle plus fin. Parfois, utiliser des modèles / arguments non typés est une meilleure approche, mais parfois ils ne le sont pas.
Vous devez penser aux cas où, par exemple, vous pourriez avoir deux signatures de méthode qui prennent chacune un
int
et unstring
comme arguments, mais où l'ordre est différent dans chaque signature. Vous pouvez très bien avoir une bonne raison de le faire, car l'implémentation de chaque signature peut faire en grande partie la même chose, mais avec une torsion légèrement différente; la journalisation peut être différente, par exemple. Ou même s'ils font exactement la même chose, vous pouvez être en mesure de glaner automatiquement certaines informations uniquement dans l'ordre dans lequel les arguments ont été spécifiés. Techniquement, vous pouvez simplement utiliser des instructions pseudo-switch pour déterminer le type de chacun des arguments transmis, mais cela devient compliqué.Alors, cet exemple suivant est-il une mauvaise pratique de programmation?
Oui, en gros. Dans cet exemple particulier, cela pourrait empêcher quelqu'un d'essayer de l'appliquer à certains types primitifs et de récupérer un comportement inattendu (ce qui pourrait être une bonne chose); mais supposons simplement que j'ai abrégé le code ci-dessus, et que vous avez en fait des surcharges pour tous les types primitifs, ainsi que pour
Object
s. Ensuite, ce bit de code suivant est vraiment plus approprié:Mais que se passe-t-il si vous n'avez besoin de l'utiliser que pour
int
s etstring
s, et si vous voulez qu'il renvoie true en fonction de conditions plus simples ou plus compliquées en conséquence? Ensuite, vous avez une bonne raison d'utiliser la surcharge:Mais bon, pourquoi ne pas simplement donner à ces fonctions deux noms différents? Vous avez toujours la même quantité de contrôle à grain fin, n'est-ce pas?
Parce que, comme indiqué précédemment, certains hôtels utilisent des chiffres, certains utilisent des lettres et certains utilisent un mélange de chiffres et de lettres:
Ce n'est toujours pas exactement le même code exact que j'utiliserais dans la vraie vie, mais cela devrait illustrer le point que je fais très bien.
Mais ... Voici pourquoi ce n'est pas plus que du sucre syntaxique dans les langues contemporaines.
Falco a soulevé le point dans les commentaires que les langages actuels ne mélangent fondamentalement pas la surcharge de méthode et la sélection de fonction dynamique dans la même étape. La façon dont j'ai précédemment compris que certaines langues fonctionnaient était que vous pouviez surcharger
appearsToBeFirstFloor
dans l'exemple ci-dessus, puis la langue déterminerait au moment de l'exécution la version de la fonction à appeler, en fonction de la valeur d'exécution de la variable non typée. Cette confusion provient en partie du travail avec des sortes de langages ECMA, comme ActionScript 3.0, dans lequel vous pouvez facilement randomiser quelle fonction est appelée sur une certaine ligne de code lors de l'exécution.Comme vous le savez peut-être, ActionScript 3 ne prend pas en charge la surcharge de méthode. Quant à VB.NET, vous pouvez déclarer et définir des variables sans affecter explicitement un type, mais lorsque vous essayez de passer ces variables comme arguments à des méthodes surchargées, il ne veut toujours pas lire la valeur d'exécution pour déterminer la méthode à appeler; il veut plutôt trouver une méthode avec des arguments de type
Object
ou aucun type ou quelque chose comme ça. Ainsi , leint
rapportstring
exemple ci - dessus ne marcherait pas dans cette langue non plus . C ++ a des problèmes similaires, car lorsque vous utilisez quelque chose comme un pointeur void ou un autre mécanisme comme celui-ci, il vous oblige toujours à lever l'ambiguïté manuellement lors de la compilation.Donc, comme le dit le premier en-tête ...
Pour les langues contemporaines, c'est juste du sucre syntaxique; d'une manière complètement indépendante de la langue, c'est plus que cela. Rendre la surcharge de méthode plus utile et plus pertinente, comme dans l'exemple ci-dessus, peut en fait être une bonne caractéristique à ajouter à un langage existant (comme cela a été largement implicitement demandé pour AS3), ou il pourrait également servir comme l'un des nombreux piliers fondamentaux pour la création d'un nouveau langage procédural / orienté objet.
la source
this[chooseFunctionNameAtRandom]();
si ellechooseFunctionNameAtRandom()
renvoie l'un ou l'autre"punch"
,"kick"
ou"dodge"
, alors vous pouvez thusly mettre en œuvre un très aléatoire simple élément, par exemple, l'IA d'un ennemi dans un jeu Flash.Cela dépend vraiment de votre définition du "sucre syntaxique". Je vais essayer de répondre à certaines des définitions qui me viennent à l'esprit:
Une fonctionnalité est du sucre syntaxique lorsqu'un programme qui l'utilise peut toujours être traduit dans un autre qui n'utilise pas la fonctionnalité.
Ici, nous supposons qu'il existe un ensemble primitif de fonctionnalités qui ne peuvent pas être traduites: en d'autres termes, aucune boucle du type "vous pouvez remplacer la fonctionnalité X en utilisant la fonctionnalité Y" et "vous pouvez remplacer la fonctionnalité Y par la fonctionnalité X". Si l'une des deux est vraie, alors l'autre caractéristique peut être exprimée en termes de caractéristiques qui ne sont pas les premières ou c'est une caractéristique primitive.
Identique à la définition 1, mais avec l'exigence supplémentaire que le programme traduit soit aussi sûr pour le type que le premier, c'est-à-dire qu'en désuchant, vous ne perdez aucune sorte d'information.
La définition de l'OP: une caractéristique est le sucre syntaxique si sa traduction ne change pas la structure du programme mais ne nécessite que des "changements locaux".
Prenons Haskell comme exemple de surcharge. Haskell fournit une surcharge définie par l'utilisateur via des classes de type. Par exemple, les opérations
+
et*
sont définies dans laNum
classe de type et tout type qui possède une instance (complète) de cette classe peut être utilisé avec+
. Par exemple:Une chose bien connue des classes de types de Haskell est que vous pouvez vous en débarrasser . C'est-à-dire que vous pouvez traduire n'importe quel programme qui utilise des classes de type dans un programme équivalent qui ne les utilise pas.
La traduction est assez simple:
Étant donné une définition de classe:
Vous pouvez le traduire en un type de données algébrique:
Ici
X_P_i
etX_op_i
sont des sélecteurs . C'est-à-dire qu'une valeur de typeX a
appliquéeX_P_1
à la valeur retournera la valeur stockée dans ce champ, donc ce sont des fonctions de typeX a -> P_i a
(ouX a -> t_i
).Pour une anologie très approximative, vous pouvez penser aux valeurs de type
X a
commestruct
s et si ifx
est de type,X a
les expressions:pourrait être considéré comme:
(Il est facile d'utiliser uniquement des champs positionnels au lieu des champs nommés, mais les champs nommés sont plus faciles à gérer dans les exemples et évitent un code de plaque chauffante).
Étant donné une déclaration d'instance:
Vous pouvez le traduire en une fonction qui, étant donné les dictionnaires des
C_1 a_1, ..., C_n a_n
classes, renvoie une valeur de dictionnaire (c'est-à-dire une valeur de typeX a
) pour le typeT a_1 ... a_n
.En d'autres termes, l'instance ci-dessus peut être traduite en une fonction comme:
(Notez que cela
n
peut être0
).Et en fait, nous pouvons le définir comme:
où
op_1 = ...
pourop_m = ...
sont les définitions figurant dans lainstance
déclaration etget_P_i_T
sont les fonctions définies par l'P_i
instance duT
genre (ceux - ci doivent exister parce queP_i
s sont superclasses deX
).Étant donné un appel à une fonction surchargée:
Nous pouvons explicitement passer les dictionnaires relatifs aux contraintes de classe et obtenir un appel équivalent:
Notez comment les contraintes de classe sont simplement devenues un nouvel argument. Le
+
dans le programme traduit est le sélecteur comme expliqué précédemment. En d'autres termes, laadd
fonction traduite , étant donné le dictionnaire pour le type de son argument, "décompressera" d'abord la fonction réelle pour calculer le résultat en utilisant(+) dictNum
puis appliquera cette fonction aux arguments.Ceci est juste un croquis très rapide sur le tout. Si vous êtes intéressé, vous devriez lire les articles de Simon Peyton Jones et al.
Je crois qu'une approche similaire pourrait également être utilisée pour la surcharge dans d'autres langues.
Cependant, cela montre que, si votre définition du sucre syntaxique est (1), alors surcharge est du sucre syntaxique . Parce que vous pouvez vous en débarrasser.
Cependant, le programme traduit perd certaines informations sur le programme d'origine. Par exemple, il n'impose pas que les instances des classes parentes existent. (Même si les opérations pour extraire les dictionnaires du parent doivent toujours être de ce type, vous pouvez passer
undefined
ou d'autres valeurs polymorphes pour pouvoir créer une valeur pourX y
sans construire les valeurs pourP_i y
, donc la traduction ne perd pas tout le type de sécurité). Ce n'est donc pas du sucre syntactique selon (2)Quant à (3). Je ne sais pas si la réponse doit être oui ou non.
Je dirais non parce que, par exemple, une déclaration d'instance devient une définition de fonction. Les fonctions surchargées obtiennent un nouveau paramètre (ce qui signifie qu'il modifie à la fois la définition et tous les appels).
Je dirais que oui, car les deux programmes sont toujours mappés l'un à l'autre, de sorte que la "structure" n'est pas vraiment modifiée.
Cela dit, je dirais que les avantages pragmatiques introduits par la surcharge sont si importants que l'utilisation d'un terme "dérogatoire" tel que "sucre syntaxique" ne semble pas correcte.
Vous pouvez traduire toute la syntaxe Haskell dans un langage Core très simple (ce qui se fait en fait lors de la compilation), de sorte que la plupart de la syntaxe Haskell pourrait être considérée comme du «sucre syntaxique» pour quelque chose qui n'est que du lambda-calcul et un peu de nouvelles constructions. Cependant, nous pouvons convenir que les programmes Haskell sont beaucoup plus faciles à gérer et sont très concis, tandis que les programmes traduits sont beaucoup plus difficiles à lire ou à penser.
la source
Si la répartition est résolue au moment de la compilation, en fonction uniquement du type statique de l'expression d'argument, vous pouvez certainement affirmer qu'il s'agit de "sucre syntaxique" remplaçant deux méthodes différentes avec des noms différents, à condition que le programmeur "connaisse" le type statique et pourrait simplement utiliser le bon nom de méthode à la place du nom surchargé. C'est aussi une forme de polymorphisme statique, mais sous cette forme limitée, il n'est généralement pas très puissant.
Bien sûr, il serait gênant de devoir changer les noms des méthodes que vous appelez chaque fois que vous changez le type d'une variable, mais par exemple dans le langage C, il est considéré comme une nuisance gérable, donc C n'a pas de surcharge de fonction (bien que il a maintenant des macros génériques).
Dans les modèles C ++, et dans n'importe quel langage qui fait une déduction de type statique non triviale, vous ne pouvez pas vraiment affirmer qu'il s'agit de "sucre syntaxique" à moins que vous ne souteniez également que la déduction de type statique est du "sucre syntaxique". Ce serait une nuisance de ne pas avoir de modèles, et dans le contexte de C ++, ce serait une "nuisance ingérable", car ils sont tellement idiomatiques pour le langage et ses bibliothèques standard. Donc en C ++ c'est plutôt plus qu'une bonne aide, c'est important pour le style du langage, et donc je pense que vous devez l'appeler plus que "sucre syntaxique".
En Java, vous pourriez considérer cela plus qu'une simple commodité en considérant par exemple le nombre de surcharges de
PrintStream.print
etPrintStream.println
. Mais alors, il existe autant deDataInputStream.readX
méthodes que Java ne surcharge pas le type de retour, donc dans un certain sens, c'est juste pour plus de commodité. Ce sont tous des types primitifs.Je ne me souviens pas ce qui se passe en Java si j'ai des classes
A
etB
prolongeantO
, je surcharger les méthodesfoo(O)
,foo(A)
etfoo(B)
, puis dans un générique que<T extends O>
j'appellefoo(t)
oùt
est une instance deT
. Dans le cas oùT
est-A
ce que j'obtiens la répartition basée sur la surcharge ou est-ce comme si j'avais appeléfoo(O)
?Dans le premier cas, les surcharges de la méthode Java sont meilleures que le sucre, de la même manière que les surcharges C ++. En utilisant votre définition, je suppose qu'en Java, je pourrais écrire localement une série de vérifications de type (ce qui serait fragile, car de nouvelles surcharges
foo
nécessiteraient des vérifications supplémentaires). En plus d'accepter cette fragilité, je ne peux pas faire de changement local sur le site d'appel pour bien faire les choses, au lieu de cela, je devrais renoncer à écrire du code générique. Je dirais que la prévention du code gonflé pourrait être du sucre syntaxique, mais la prévention du code fragile est plus que cela. Pour cette raison, le polymorphisme statique en général est plus que du sucre syntaxique. La situation dans une langue particulière peut être différente, selon la mesure dans laquelle la langue vous permet de "ne pas connaître" le type statique.la source
T:Animal
c'est est de typeSiameseCat
et existants sont surchargesCat Foo(Animal)
,SiameseCat Foo(Cat)
etAnimal Foo(SiameseCat)
qui surcharge doit être sélectionné siT
estSiameseCat
?long foo=Math.round(bar*1.0001)*5
soit modifiée enlong foo=Math.round(bar)*5
. Comment cela affecterait-il la sémantique s'il étaitbar
égal, par exemple, 123456789L?long
àdouble
.double
?Il semble que le "sucre syntaxique" semble désobligeant, inutile ou frivole. C'est pourquoi la question déclenche de nombreuses réponses négatives.
Mais vous avez raison, la surcharge de méthode n'ajoute aucune fonctionnalité au langage, à l'exception de la possibilité d'utiliser le même nom pour différentes méthodes. Vous pouvez rendre le type de paramètre explicite, le programme fonctionnera toujours de la même manière.
Il en va de même pour les noms de package. La chaîne n'est qu'un sucre syntaxique pour java.lang.String.
En fait, une méthode comme
dans la classe MyClass devrait s'appeler quelque chose comme "my_package_MyClass_fun_int_java_lang_String". Cela identifierait la méthode de manière unique. (La JVM fait quelque chose comme ça en interne). Mais vous ne voulez pas écrire ça. C'est pourquoi le compilateur vous permettra d'écrire fun (1, "one") et d'identifier de quelle méthode il s'agit.
Il y a cependant une chose que vous pouvez faire avec la surcharge: si vous surchargez une méthode avec le même nombre d'arguments, le compilateur trouvera automatiquement la version qui convient le mieux à l'argument donné en faisant correspondre les arguments, non seulement avec des types égaux, mais aussi où l'argument donné est une sous-classe de l'argument déclaré.
Si vous avez deux procédures surchargées
vous n'avez pas besoin de savoir qu'il existe une version spécifique de la procédure pour les dates. addParameter ("hello", "world) appellera la première version, addParameter (" now ", new Date ()) appellera la seconde.
Bien sûr, vous devez éviter de surcharger une méthode avec une autre méthode qui fait une chose complètement différente.
la source
Fait intéressant, la réponse à cette question dépendra de la langue.
Plus précisément, il existe une interaction entre la surcharge et la programmation générique (*), et selon la façon dont la programmation générique est implémentée, il peut s'agir simplement de sucre syntaxique (Rust) ou absolument nécessaire (C ++).
Autrement dit, lorsque la programmation générique est implémentée avec des interfaces explicites (dans Rust ou Haskell, ce sont des classes de type), alors la surcharge n'est que du sucre syntaxique; ou pourrait même ne pas faire partie de la langue.
D'un autre côté, lorsque la programmation générique est implémentée avec le typage canard (dynamique ou statique), le nom de la méthode est un contrat essentiel, et donc la surcharge est obligatoire pour que le système fonctionne.
(*) Utilisé dans le sens d'écrire une méthode une fois, pour fonctionner sur différents types de manière uniforme.
la source
Dans certaines langues, il ne s'agit sans doute que de sucre syntaxique. Cependant, ce dont il s'agit est un sucre qui dépend de votre point de vue. Je vais laisser cette discussion pour plus tard dans cette réponse.
Pour l'instant, je voudrais juste noter que dans certaines langues, ce n'est certainement pas du sucre syntaxique. Du moins pas sans vous obliger à utiliser une logique / algorithme complètement différent pour implémenter la même chose. C'est comme affirmer que la récursivité est du sucre syntaxique (ce qui est le cas puisque vous pouvez écrire tous les algorithmes récursifs avec une boucle et une pile).
Un exemple d'une utilisation très difficile à remplacer vient d'un langage qui, ironiquement, n'appelle pas cette fonctionnalité "surcharge de fonction". Au lieu de cela, on l'appelle "pattern matching" (qui peut être considéré comme un surensemble de surcharge car nous pouvons surcharger non seulement les types mais les valeurs).
Voici l'implémentation naïve classique de la fonction Fibonacci dans Haskell:
On peut dire que les trois fonctions peuvent être remplacées par un if / else comme cela se fait couramment dans n'importe quelle autre langue. Mais cela rend fondamentalement la définition tout à fait simple:
beaucoup plus compliqué et n'exprime pas directement la notion mathématique de la séquence de Fibonacci.
Il peut donc parfois s'agir de sucre de syntaxe si la seule utilisation est de vous permettre d'appeler une fonction avec des arguments différents. Mais parfois, c'est beaucoup plus fondamental que cela.
Maintenant, pour la discussion de ce que la surcharge de l'opérateur peut être un sucre. Vous avez identifié un cas d'utilisation - il peut être utilisé pour implémenter des fonctions similaires qui prennent différents arguments. Donc:
peut également être mis en œuvre comme:
ou même:
Mais la surcharge d'opérateur peut également être un sucre pour l'implémentation d'arguments optionnels (certaines langues ont une surcharge d'opérateur mais pas d'arguments optionnels):
peut être utilisé pour implémenter:
Dans une telle langue (google "langue Ferite"), la suppression de la surcharge de l'opérateur supprime considérablement une fonctionnalité - les arguments facultatifs. Accordé dans les langues avec les deux fonctionnalités (c ++), la suppression de l'une ou de l'autre n'aura aucun effet net car l'une ou l'autre peut être utilisée pour implémenter des arguments facultatifs.
la source
data PaymentInfo = CashOnDelivery | Adress String | UserInvoice CustomerInfo
vous pouvez faire correspondre les modèles sur les constructeurs de types.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
. J'espère que ce commentaire est quelque peu compréhensible.Je pense que c'est du sucre syntaxique simple dans la plupart des langues (du moins tout ce que je sais ...) car elles nécessitent toutes un appel de fonction sans ambiguïté au moment de la compilation. Et le compilateur remplace simplement l'appel de fonction par un pointeur explicite vers la bonne signature d'implémentation.
Exemple en Java:
Donc, à la fin, il pourrait être complètement remplacé par une simple macro de compilation avec recherche et remplacement, remplaçant la fonction mangle surchargée par mangle_String et mangle_int - puisque la liste d'arguments fait partie de l'identificateur de fonction éventuel, c'est pratiquement ce qui se passe -> et il ne s'agit donc que de sucre syntaxique.
Maintenant, s'il y a un langage, où la fonction est vraiment déterminée au moment de l'exécution, comme avec les méthodes substituées dans les objets, ce serait différent. Mais je ne pense pas qu'il existe un tel langage, car method.overloading est sujet à ambiguïté, que le compilateur ne peut pas résoudre et qui doit être géré par le programmeur avec un cast explicite. Cela ne peut pas être fait lors de l'exécution.
la source
En Java, les informations sont compilées et celle des surcharges qui est appelée est décidée au moment de la compilation.
Ce qui suit est un extrait de
sun.misc.Unsafe
(l'utilitaire pour Atomics) tel qu'il est affiché dans l'éditeur de fichiers de classe d'Eclipse.comme vous pouvez le voir, les informations de type de la méthode appelée (ligne 4) sont incluses dans l'appel.
Cela signifie que vous pouvez créer un compilateur java qui prend les informations de type. Par exemple, en utilisant une telle notation, la source ci-dessus serait alors:
et le casting à long serait facultatif.
Dans d'autres langages compilés de type statique, vous verrez une configuration similaire où le compilateur décidera quelle surcharge sera appelée en fonction des types et l'inclura dans la liaison / l'appel.
L'exception est les bibliothèques dynamiques C où les informations de type ne sont pas incluses et si vous essayez de créer une fonction surchargée, l'éditeur de liens se plaindra.
la source