En venant de C ++ à Java, la question évidente sans réponse est pourquoi Java n'a-t-il pas inclus la surcharge de l'opérateur?
N'est-ce pas Complex a, b, c; a = b + c;
beaucoup plus simple que Complex a, b, c; a = b.add(c);
?
Y a-t-il une raison connue à cela, des arguments valables pour ne pas autoriser la surcharge de l'opérateur? La raison est-elle arbitraire ou perdue dans le temps?
java
operator-overloading
rengolin
la source
la source
Réponses:
En supposant que vous vouliez remplacer la valeur précédente de l'objet référencé par
a
, alors une fonction membre devrait être invoquée.En C ++, cette expression indique au compilateur de créer trois (3) objets sur la pile, d'effectuer l'ajout et de copier la valeur résultante de l'objet temporaire dans l'objet existant
a
.Cependant, en Java,
operator=
n'effectue pas de copie de valeur pour les types de référence, et les utilisateurs peuvent uniquement créer de nouveaux types de référence, pas des types de valeur. Ainsi, pour un type défini par l'utilisateur nomméComplex
, l'affectation signifie copier une référence vers une valeur existante.Considérez plutôt:
En C ++, cela copie la valeur, donc la comparaison ne sera pas égale. En Java,
operator=
effectue une copie de référence, donca
etb
fait maintenant référence à la même valeur. En conséquence, la comparaison produira «égal», puisque l'objet se comparera égal à lui-même.La différence entre les copies et les références ne fait qu'ajouter à la confusion de la surcharge de l'opérateur. Comme @Sebastian l'a mentionné, Java et C # doivent tous deux traiter séparément l'égalité des valeurs et des références -
operator+
traiteraient probablement des valeurs et des objets, maisoperator=
sont déjà implémentés pour gérer les références.En C ++, vous ne devriez avoir affaire qu'à un seul type de comparaison à la fois, donc cela peut être moins déroutant. Par exemple, sur
Complex
,operator=
etoperator==
travaillent tous deux sur des valeurs - la copie des valeurs et la comparaison des valeurs respectivement.la source
Il y a beaucoup de messages qui se plaignent de la surcharge des opérateurs.
J'ai senti que je devais clarifier les concepts de «surcharge de l'opérateur», en offrant un point de vue alternatif sur ce concept.
Code obscurcissant?
Cet argument est une erreur.
L'obscurcissement est possible dans toutes les langues ...
Il est aussi facile d'obscurcir le code en C ou Java via des fonctions / méthodes qu'en C ++ via des surcharges d'opérateurs:
... même dans les interfaces standard de Java
Pour un autre exemple, voyons l'
Cloneable
interface en Java:Vous êtes censé cloner l'objet implémentant cette interface. Mais tu pourrais mentir. Et créez un objet différent. En fait, cette interface est si faible que vous pourriez renvoyer un autre type d'objet, juste pour le plaisir:
Comme le
Cloneable
interface peut être utilisée abusivement / obscurcie, devrait-elle être interdite pour les mêmes motifs que la surcharge de l'opérateur C ++ est censée être?Nous pourrions surcharger la
toString()
méthode d'uneMyComplexNumber
classe pour qu'elle retourne l'heure stringifiée de la journée. LatoString()
surcharge devrait-elle également être interdite? On pourrait saboterMyComplexNumber.equals
pour lui faire retourner une valeur aléatoire, modifier les opérandes ... etc etc etc ..En Java, comme en C ++, ou dans n'importe quel langage, le programmeur doit respecter un minimum de sémantique lors de l'écriture de code. Cela signifie implémenter une
add
fonction qui ajoute, et uneCloneable
méthode d'implémentation qui clone, et un++
opérateur qui incrémente.Qu'est-ce qui obscurcit de toute façon?
Maintenant que nous savons que le code peut être saboté même à travers les méthodes Java immaculées, nous pouvons nous interroger sur l'utilisation réelle de la surcharge d'opérateur en C ++?
Notation claire et naturelle: méthodes vs surcharge de l'opérateur?
Nous comparerons ci-dessous, pour différents cas, le "même" code en Java et C ++, pour avoir une idée du type de style de codage le plus clair.
Comparaisons naturelles:
Veuillez noter que A et B peuvent être de tout type en C ++, tant que les surcharges d'opérateur sont fournies. En Java, lorsque A et B ne sont pas des primitives, le code peut devenir très déroutant, même pour des objets de type primitif (BigInteger, etc.) ...
Accesseurs naturels de tableau / conteneur et indice:
En Java, nous voyons que pour que chaque conteneur fasse la même chose (accéder à son contenu via un index ou un identifiant), nous avons une manière différente de le faire, ce qui est déroutant.
En C ++, chaque conteneur utilise de la même manière pour accéder à son contenu, grâce à la surcharge de l'opérateur.
Manipulation naturelle des types avancés
Les exemples ci-dessous utilisent un
Matrix
objet, trouvé en utilisant les premiers liens trouvés sur Google pour " objet Matrix Java " et " objet Matrix C ++ ":Et cela ne se limite pas aux matrices. Le
BigInteger
etBigDecimal
classes de Java souffrent de la même verbosité déroutante, tandis que leurs équivalents en C ++ sont aussi clairs que les types intégrés.Itérateurs naturels:
Foncteurs naturels:
Concaténation de texte:
Ok, en Java, vous pouvez utiliser
MyString = "Hello " + 25 + " World" ;
aussi ... Mais, attendez une seconde: c'est la surcharge de l'opérateur, n'est-ce pas? N'est-ce pas de la triche ???:-RÉ
Code générique?
Les mêmes opérandes de modification de code générique doivent être utilisables à la fois pour les objets intégrés / primitifs (qui n'ont pas d'interface en Java), les objets standard (qui ne peuvent pas avoir la bonne interface) et les objets définis par l'utilisateur.
Par exemple, calculer la valeur moyenne de deux valeurs de types arbitraires:
Discuter de la surcharge de l'opérateur
Maintenant que nous avons vu des comparaisons équitables entre le code C ++ utilisant la surcharge d'opérateur et le même code en Java, nous pouvons maintenant discuter de la "surcharge d'opérateur" en tant que concept.
La surcharge des opérateurs existait depuis avant les ordinateurs
Même en dehors de la science informatique, il y a surcharge de l' opérateur: Par exemple, en mathématiques, des opérateurs comme
+
,-
,*
, etc. sont surchargés.En effet, la signification
+
,-
,*
, etc. change en fonction des types des opérandes (Numerics, vecteurs, fonctions d'onde quantique, matrices, etc.).La plupart d'entre nous, dans le cadre de nos cours de sciences, ont appris plusieurs significations pour les opérateurs, selon les types d'opérandes. Les avons-nous trouvés déroutants, eux?
La surcharge de l'opérateur dépend de ses opérandes
C'est la partie la plus importante de la surcharge des opérateurs: comme en mathématiques ou en physique, l'opération dépend du type de ses opérandes.
Donc, connaissez le type de l'opérande, et vous connaîtrez l'effet de l'opération.
Même C et Java ont une surcharge d'opérateur (codée en dur)
En C, le comportement réel d'un opérateur changera en fonction de ses opérandes. Par exemple, l'ajout de deux entiers est différent de l'ajout de deux doubles, ou même d'un entier et d'un double. Il y a même tout le domaine arithmétique du pointeur (sans transtypage, vous pouvez ajouter à un pointeur un entier, mais vous ne pouvez pas ajouter deux pointeurs ...).
En Java, il n'y a pas d'arithmétique de pointeur, mais quelqu'un a quand même trouvé que la concaténation de chaînes sans l'
+
opérateur serait assez ridicule pour justifier une exception dans le credo "la surcharge de l'opérateur est un mal".C'est juste que vous, en tant que codeur C (pour des raisons historiques) ou Java (pour des raisons personnelles , voir ci-dessous), vous ne pouvez pas fournir le vôtre.
En C ++, la surcharge d'opérateur n'est pas facultative ...
En C ++, la surcharge d'opérateur pour les types intégrés n'est pas possible (et c'est une bonne chose), mais les types définis par l' utilisateur peuvent avoir définis par l'utilisateur surcharges d'opérateur .
Comme déjà dit précédemment, en C ++, et contrairement à Java, les types d'utilisateurs ne sont pas considérés comme des citoyens de seconde classe du langage, par rapport aux types intégrés. Donc, si les types intégrés ont des opérateurs, les types d'utilisateurs devraient également pouvoir les avoir.
La vérité est que, comme
toString()
,clone()
, lesequals()
méthodes sont pour Java ( par exemple quasi-standard comme ), la surcharge de l' opérateur de C est tellement partie de C ++ qu'il devient aussi naturel que les opérateurs d' origine C, ou avant des méthodes Java mentionnées.Combinée à la programmation de modèles, la surcharge de l'opérateur devient un modèle de conception bien connu. En fait, vous ne pouvez pas aller très loin dans STL sans utiliser des opérateurs surchargés et des opérateurs de surcharge pour votre propre classe.
... mais il ne faut pas en abuser
La surcharge de l'opérateur doit s'efforcer de respecter la sémantique de l'opérateur. Ne pas soustraire dans un
+
opérateur (comme dans "ne pas soustraire dans uneadd
fonction", ou "renvoyer des conneries dans uneclone
méthode").La surcharge de plâtre peut être très dangereuse car elle peut conduire à des ambiguïtés. Ils devraient donc vraiment être réservés à des cas bien définis. En ce qui concerne
&&
et||
, ne savent jamais les surcharger , sauf si vous vraiment ce que vous faites, que vous perdrez l'évaluation de court-circuit que les opérateurs natifs&&
et||
profiter.Alors ... Ok ... Alors pourquoi ce n'est pas possible en Java?
Parce que James Gosling l'a dit:
Veuillez comparer le texte de Gosling ci-dessus avec Stroustrup ci-dessous:
La surcharge de l'opérateur serait-elle avantageuse pour Java?
Certains objets bénéficieraient grandement de la surcharge des opérateurs (types concrets ou numériques, comme BigDecimal, nombres complexes, matrices, conteneurs, itérateurs, comparateurs, analyseurs, etc.).
En C ++, vous pouvez profiter de cet avantage en raison de l'humilité de Stroustrup. En Java, vous êtes simplement foutu à cause du choix personnel de Gosling .
Pourrait-il être ajouté à Java?
Les raisons de ne pas ajouter de surcharge d'opérateur maintenant à Java pourraient être un mélange de politique interne, d'allergie à la fonctionnalité, de méfiance envers les développeurs (vous savez, les saboteurs qui semblent hanter les équipes Java ...), la compatibilité avec les JVM précédentes, le temps d'écrire une spécification correcte, etc.
Alors ne retenez pas votre souffle en attendant cette fonctionnalité ...
Mais ils le font en C # !!!
Ouais...
Si c'est loin d'être la seule différence entre les deux langues, celle-ci ne manque jamais de m'amuser.
Apparemment, les gens C #, avec leur "chaque primitive est un
struct
, et unstruct
dérive d'Object" , ont bien fait du premier coup.Et ils le font dans d' autres langues !!!
Malgré tous les FUD contre la surcharge d'opérateur définie utilisée, les langages suivants le prennent en charge: 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 ...
Tant de langues, avec tant de philosophies différentes (et parfois opposées), et pourtant elles sont toutes d'accord sur ce point.
Nourriture pour la pensée...
la source
James Gosling a comparé la conception de Java à ce qui suit:
Vous pouvez lire le contexte de la citation ici
Fondamentalement, la surcharge de l'opérateur est idéale pour une classe qui modélise une sorte de point, de devise ou de nombre complexe. Mais après cela, vous commencez à manquer d'exemples rapidement.
Un autre facteur a été l'abus de la fonctionnalité en C ++ par des développeurs surchargeant des opérateurs comme '&&', '||', les opérateurs de cast et bien sûr 'new'. La complexité résultant de la combinaison de cela avec la valeur de passage et les exceptions est bien couverte dans le livre Exceptional C ++ .
la source
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)
.I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language
: Les mauvais programmeurs écriront du mauvais code, peu importe la langue. Essayez juste d'émuler une "passe-par-référence" pour que les paramètres de fonction en Java aient une idée. J'ai vu le code et j'ai ri si fort que ça m'a fait mal. C'est le genre de choses que Gosling n'a pas utilisé, donc, il fallait des horribles hacks pour Java, pourtant, il existe nativement, à un coût nul, en C # et C ++.Découvrez Boost.Units: texte du lien
Il fournit une analyse dimensionnelle sans frais généraux grâce à une surcharge de l'opérateur. Jusqu'à quel point cela peut-il être plus clair?
produirait en fait "Energy = 4 J", ce qui est correct.
la source
Les concepteurs Java ont décidé que la surcharge des opérateurs était plus problématique qu'elle n'en valait la peine. Aussi simple que cela.
Dans un langage où chaque variable objet est en fait une référence, la surcharge d'opérateur présente le risque supplémentaire d'être tout à fait illogique - pour un programmeur C ++ au moins. Comparez la situation avec la surcharge de l'opérateur == de C # et
Object.Equals
etObject.ReferenceEquals
(ou tout autre nom).la source
Groovy a une surcharge d'opérateur et s'exécute dans la JVM. Si cela ne vous dérange pas, la performance (qui diminue de jour en jour). Il est automatique en fonction des noms de méthode. par exemple, '+' appelle la méthode 'plus (argument)'.
la source
where ...
devient.Where(i => ...
). Si seulement ils avaient fait de même avec les opérateurs arithmétiques, tant de choses seraient plus simples et plus puissantes. Java a l'avantage d'une table rase et pourrait bien faire les choses (bien que pour des raisons religieuses, il ne le sera probablement jamais).Je pense que cela a peut-être été un choix de conception conscient pour forcer les développeurs à créer des fonctions dont les noms communiquent clairement leurs intentions. En C ++, les développeurs surchargeraient les opérateurs de fonctionnalités qui n'auraient souvent aucun rapport avec la nature communément acceptée de l'opérateur donné, ce qui rend presque impossible de déterminer ce qu'un code fait sans regarder la définition de l'opérateur.
la source
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator
: Ceci est une affirmation gratuite. Je suis développeur C ++ professionnel depuis 12 ans et j'ai rarement rencontré ce problème. En fait, la plupart des bogues et des erreurs de conception que j'ai vus en C ++ se trouvaient dans le code de style C (void *
, transtypages, etc.)add
fonction pourrait être vraiment vraiment mal utilisée (comme faire une multiplication, ou acquérir un mutex) ... L'abus mentionné par user14128 n'est pas limité aux opérateurs, mais il y a une sorte de peur pathologique de la surcharge des opérateurs qui, je pense, vient des premiers jours de C contre C ++, une peur qui n'a pas été modifiée directement en Java, mais heureusement, n'est pas entrée en C # ... En fin de compte, en respectant la sémantique et écrire des fonctions / opérateurs clairs est le travail du développeur. Pas la langue.cout << f() || g();
les parenthèses ne le rendent pas plus clair, elles le rendent correct. Et si les opérateurs de décalage de bits n'étaient pas abusés, ils ne seraient pas nécessaires. Pourquoi vautcout << (5&3) << endl;
mieux quecout.fmt(5&3)(endl);
? L'utilisation de l'opérateur d'appel de fonction sur une variable membre du foncteur serait une conception infiniment meilleure pour les flux que la réorientation des opérateurs au niveau du bit simplement parce que le glyphe est joli. Mais c'est loin d'être le seul problème avec les streams.Eh bien, vous pouvez vraiment vous tirer une balle dans le pied avec une surcharge de l'opérateur. C'est comme avec les pointeurs, les gens font des erreurs stupides avec eux et il a donc été décidé d'enlever les ciseaux.
Du moins, je pense que c'est la raison. Je suis de ton côté de toute façon. :)
la source
Certaines personnes disent que la surcharge des opérateurs en Java conduirait à l'obscurcissement. Ces personnes se sont-elles jamais arrêtées pour regarder du code Java faisant des calculs de base comme augmenter une valeur financière d'un pourcentage en utilisant BigDecimal? .... la verbosité d'un tel exercice devient sa propre démonstration d'obscurcissement. Ironiquement, l'ajout d'une surcharge d'opérateur à Java nous permettrait de créer notre propre classe Currency, ce qui rendrait ce code mathématique élégant et simple (moins obscurci).
la source
Dire que la surcharge de l'opérateur entraîne des erreurs logiques de type tel que l'opérateur ne correspond pas à la logique de fonctionnement, c'est comme ne rien dire. Le même type d'erreur se produira si le nom de la fonction est inapproprié pour la logique de fonctionnement - alors quelle est la solution: abandonner la capacité d'utilisation de la fonction!? Ceci est une réponse comique - "inapproprié pour la logique d'opération", chaque nom de paramètre, chaque classe, fonction ou autre peut être logiquement inapproprié. Je pense que cette option devrait être disponible dans un langage de programmation respectable, et ceux qui pensent que ce n'est pas sûr - hé, aucun ne dit que vous devez l'utiliser. Prenons le C #. Ils ont baissé les pointeurs mais bon - il y a une déclaration de «code dangereux» - programmez comme vous le souhaitez à vos propres risques.
la source
Techniquement, il y a une surcharge d'opérateur dans chaque langage de programmation qui peut traiter différents types de nombres, par exemple des nombres entiers et réels. Explication: Le terme surcharge signifie qu'il existe simplement plusieurs implémentations pour une fonction. Dans la plupart des langages de programmation, différentes implémentations sont fournies pour l'opérateur +, une pour les entiers, une pour les réels, c'est ce qu'on appelle la surcharge de l'opérateur.
Maintenant, beaucoup de gens trouvent étrange que Java ait une surcharge d'opérateur pour l'opérateur + pour ajouter des chaînes ensemble, et d'un point de vue mathématique, ce serait vraiment étrange, mais vu du point de vue du développeur d'un langage de programmation, il n'y a rien de mal à ajouter une surcharge d'opérateur intégrée pour l'opérateur + pour d'autres classes, par exemple String. Cependant, la plupart des gens conviennent qu'une fois que vous ajoutez une surcharge intégrée pour + pour String, il est généralement préférable de fournir également cette fonctionnalité au développeur.
Un désaccord complet avec l'erreur selon laquelle la surcharge de l'opérateur obscurcit le code, car c'est au développeur d'en décider. C'est naïf de penser, et pour être honnête, ça vieillit.
+1 pour l'ajout d'une surcharge d'opérateur dans Java 8.
la source
+
pour concaténer n'importe quoi de chaîne est à mon humble avis, tout comme la surcharge de/
C et FORTRAN pour la division entière et fractionnée. Dans de nombreuses versions de Pascal, l'utilisation d'opérateurs arithmétiques sur n'importe quel type numérique produira des résultats numériquement équivalents à la conversion des opérandesReal
, bien que les résultats qui pourraient ne pas être des nombres entiers doivent être transmisTrunc
ouRound
avant qu'ils puissent être affectés à des entiers.En supposant que Java soit le langage d'implémentation, alors a, b et c seraient tous des références au type Complex avec des valeurs initiales nulles. En supposant également que le complexe est immuable en tant que BigInteger mentionné et BigDecimal immuable similaire , je pense que vous voulez dire ce qui suit, car vous attribuez la référence au complexe renvoyé par l'ajout de b et c, et ne comparez pas cette référence à a.
la source
Parfois, ce serait bien d'avoir une surcharge d'opérateur, des classes d'amis et un héritage multiple.
Cependant, je pense toujours que c'était une bonne décision. Si Java avait eu une surcharge d'opérateur, nous ne pourrions jamais être sûrs de la signification de l'opérateur sans regarder à travers le code source. Pour l'instant, ce n'est pas nécessaire. Et je pense que votre exemple d'utilisation de méthodes au lieu d'une surcharge d'opérateur est également assez lisible. Si vous voulez clarifier les choses, vous pouvez toujours ajouter un commentaire au-dessus des déclarations velues.
la source
Ce n'est pas une bonne raison de le refuser mais une raison pratique:
Les gens ne l'utilisent pas toujours de manière responsable. Regardez cet exemple de la scapy de la bibliothèque Python:
Voici l'explication:
la source
Alternatives à la prise en charge native de la surcharge des opérateurs Java
Comme Java n'a pas de surcharge d'opérateur, voici quelques alternatives que vous pouvez étudier:
Si quelqu'un est au courant des autres, veuillez commenter et je l'ajouterai à cette liste.
la source
Bien que le langage Java ne prenne pas directement en charge la surcharge des opérateurs, vous pouvez utiliser le plug-in de compilation Manifold dans n'importe quel projet Java pour l'activer. Il prend en charge Java 8-13 (la version Java actuelle) et est entièrement pris en charge dans IntelliJ IDEA.
la source