J'ai lu cet article: Comment écrire une méthode d'égalité en Java .
Fondamentalement, il fournit une solution pour une méthode equals () qui prend en charge l'héritage:
Point2D twoD = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true
Mais est-ce une bonne idée? ces deux instances semblent être égales mais peuvent avoir deux codes de hachage différents. N'est-ce pas un peu faux?
Je crois que cela serait mieux réalisé en moulant les opérandes à la place.
java
c#
scala
comparison
Nous s
la source
la source
z
coordonnée zéro peut être une convention utile pour certaines applications (les premiers systèmes de CAO gérant les données héritées viennent à l'esprit). Mais c'est une convention arbitraire. Les plans dans des espaces de 3 dimensions ou plus peuvent avoir des orientations arbitraires ... c'est ce qui rend les problèmes intéressants intéressants.Réponses:
Cela ne devrait pas être l'égalité car elle rompt la transitivité . Considérez ces deux expressions:
L'égalité étant transitive, cela devrait signifier que l'expression suivante est également vraie:
Mais bien sûr - ce n'est pas le cas.
Donc, votre idée de coulée est correcte - attendez-vous à ce que, en Java, la coulée signifie simplement couler le type de la référence. Ce que vous voulez vraiment ici, c'est une méthode de conversion qui créera un nouvel
Point2D
objet à partir d'unPoint3D
objet. Cela rendrait également l'expression plus significative:la source
(10, 20, 50)
égal(10, 20, 60)
est très bien. Nous nous soucions seulement de10
et20
.Point2D
- il avoir uneprojectXYZ()
méthode pour sePoint3D
représenter soi-même? En d'autres termes, les implémentations doivent-elles se connaître?Point2D
semble plus simple car la projection de points 2D nécessite de définir d'abord leur plan dans l'espace 3D. Si le point 2D sait qu'il est plan, c'est déjà un point 3D. Sinon, il ne peut pas projeter. Je me souviens du Flatland d'Abbott .Plane3D
objet, qui définira un plan dans l'espace 3D, ce plan peut avoir unelift
méthode (2D-> 3D se soulève, ne se projette pas) qui acceptera unPoint2D
et un nombre pour le "troisième axe" "- distance de l'avion le long de l'avion normal. Pour faciliter l'utilisation, vous pouvez définir les plans communs comme des constantes statiques, de sorte que vous pouvez faire des choses commePlane3D.XY.lift(new Point2D(10, 20), 50).equals(new Point3D(10, 20, 50))
Je m'éloigne de la lecture de l'article en pensant à la sagesse d'Alan J. Perlis:
Le fait que le droit à «l'égalité» soit le genre de problème qui empêche l' inventeur de Martin Scala de Scala de dormir la nuit devrait faire réfléchir sur la question de savoir si le remplacement
equals
dans un arbre d'héritage est une bonne idée.Ce qui se passe lorsque nous n'avons pas de chance d'obtenir un,
ColoredPoint
c'est que notre géométrie échoue parce que nous avons utilisé l'héritage pour proliférer les types de données plutôt que d'en créer un bon. Ceci malgré le fait de devoir revenir en arrière et de modifier le nœud racine de l'arbre d'héritage pour que celaequals
fonctionne. Pourquoi ne pas simplement ajouter unz
et uncolor
àPoint
?La raison serait que
Point
etColoredPoint
fonctionner dans différents domaines ... au moins si ces domaines ne se mêlent. Pourtant, si c'est le cas, nous n'avons pas besoin de passer outreequals
. ComparerColoredPoint
etPoint
pour l'égalité n'a de sens que dans un troisième domaine où ils sont autorisés à se mêler. Et dans ce cas, il est probablement préférable d'avoir «l'égalité» adaptée à ce troisième domaine plutôt que d'essayer d'appliquer la sémantique d'égalité de l'un ou l'autre ou des deux domaines non mélangés. En d'autres termes, «l'égalité» devrait être définie localement à l'endroit où nous avons de la boue qui coule des deux côtés, car nous ne voulons peut-être pasColoredPoint.equals(pt)
échouer contre des cas dePoint
même si l'auteur de laColoredPoint
pensée était une bonne idée il y a six mois à 2 heures du matin. .la source
Lorsque les anciens dieux de la programmation inventaient la programmation orientée objet avec les classes, ils décidaient, en matière de composition et d'héritage, d'avoir deux relations pour un objet: "est un" et "a un".
Cela a partiellement résolu le problème des sous-classes différentes des classes parentes mais les a rendues utilisables sans casser le code. Parce qu'une instance de sous-classe "est un" objet de super-classe et peut y être substitué directement, même si la sous-classe a plus de fonctions membres ou de membres de données, le "a a" garantit qu'il exécutera toutes les fonctions du parent et aura toutes ses fonctions. membres. On pourrait donc dire qu'un Point3D "est un" Point, et un Point2D "est un" Point s'ils héritent tous les deux de Point. De plus, un Point3D pourrait être une sous-classe de Point2D.
Cependant, l'égalité entre les classes est spécifique au domaine du problème et l'exemple ci-dessus est ambigu quant à ce dont le programmeur a besoin pour que le programme fonctionne correctement. En règle générale, les règles du domaine mathématique sont suivies et les valeurs des données génèrent une égalité si vous limitez la portée de la comparaison à deux dimensions dans ce cas, mais pas si vous comparez tous les membres des données.
Vous obtenez donc un tableau de réduction des égalités:
Vous choisissez généralement les règles les plus strictes que vous pouvez encore exécuter toutes les fonctions nécessaires dans votre domaine problématique. Les tests d'égalité intégrés pour les nombres sont conçus pour être aussi restrictifs que possible à des fins mathématiques, mais le programmeur a de nombreuses façons de contourner cela si ce n'est pas le but, y compris l'arrondi vers le haut / vers le bas, la troncature, gt, lt, etc. . Les objets avec horodatage sont souvent comparés en fonction de leur temps de génération et chaque instance doit donc être unique pour que les comparaisons deviennent très spécifiques.
Le facteur de conception dans ce cas est de déterminer des moyens efficaces de comparer des objets. Parfois, une comparaison récursive de tous les objets membres de données est ce que vous devez faire, et cela peut devenir très coûteux si vous avez beaucoup, beaucoup d'objets avec beaucoup de membres de données. Les alternatives consistent à comparer uniquement les valeurs de données pertinentes, ou à faire générer par l'objet une valeur de hachage de ses membres de données concernés pour une comparaison rapide avec d'autres objets similaires, à conserver les collections triées et élaguées pour rendre les comparaisons plus rapides et moins gourmandes en CPU, et peut-être à autoriser les objets qui sont identiques dans les données à éliminer et un pointeur en double vers un seul objet soit mis à sa place.
la source
La règle est, chaque fois que vous remplacez
hashcode()
, vous remplacezequals()
et vice versa. Que ce soit une bonne idée ou non dépend de l'utilisation prévue. Personnellement, j'irais avec une méthode différente (isLike()
ou similaire) pour obtenir le même effet.la source
Il est souvent utile pour les classes non publiques d'avoir une méthode de test d'équivalence qui permet aux objets de différents types de se considérer "égaux" s'ils représentent les mêmes informations, mais parce que Java ne permet aucun moyen par lequel les classes peuvent se faire passer pour chacune Dans d'autres cas, il est souvent bon d'avoir un seul type de wrapper ouvert au public dans les cas où il pourrait être possible d'avoir des objets équivalents avec des représentations différentes.
Par exemple, considérons une classe encapsulant une matrice 2D immuable de
double
valeurs. Si une méthode extérieure demande une matrice d'identité de taille 1000, une seconde demande une matrice diagonale et passe un tableau contenant 1000 unités, et une troisième demande une matrice 2D et passe un tableau 1000x1000 où les éléments de la diagonale principale sont tous 1.0 et tous les autres sont nuls, les objets donnés aux trois classes peuvent utiliser différents magasins de sauvegarde en interne [le premier ayant un seul champ pour la taille, le second ayant un tableau de mille éléments et le troisième ayant mille tableaux de 1000 éléments] mais devraient se rapporter comme équivalents [puisque les trois encapsulent une matrice immuable de 1000x1000 avec des sur la diagonale et des zéros partout ailleurs].Au-delà du fait qu'il masque l'existence de types de stockage distincts, le wrapper sera également utile pour faciliter les comparaisons, car la vérification de l'équivalence des éléments sera généralement un processus en plusieurs étapes. Demandez au premier élément s'il sait s'il est égal au second; s'il ne sait pas, demandez au second s'il sait s'il est égal au premier. Si aucun objet ne le sait, demandez à chaque tableau le contenu de ses éléments individuels [on pourrait ajouter d'autres vérifications avant de décider de faire la route de comparaison individuelle longue-lente].
Notez que la méthode de test d'équivalence pour chaque objet de ce scénario devrait renvoyer une valeur à trois états ("Oui, je suis équivalent", "Non, je ne suis pas équivalent" ou "Je ne sais pas"), donc la méthode normale "égale" ne conviendrait pas. Alors que n'importe quel objet pourrait simplement répondre "Je ne sais pas" lorsqu'on lui en pose la question, ajouter une logique, par exemple à une matrice diagonale qui ne dérangerait pas de demander à une matrice d'identité ou à une matrice diagonale des éléments hors de la diagonale principale, accélérerait considérablement les comparaisons entre ces les types.
la source