Quels problèmes / pièges doivent être pris en compte lors de la substitution equals
et hashCode
?
la source
Quels problèmes / pièges doivent être pris en compte lors de la substitution equals
et hashCode
?
equals()
( javadoc ) doit définir une relation d'équivalence (elle doit être réflexive , symétrique et transitive ). De plus, il doit être cohérent (si les objets ne sont pas modifiés, alors il doit continuer à renvoyer la même valeur). En outre, o.equals(null)
doit toujours retourner faux.
hashCode()
( javadoc ) doit également être cohérent (si l'objet n'est pas modifié en termes de equals()
, il doit continuer à renvoyer la même valeur).
La relation entre les deux méthodes est:
Chaque fois
a.equals(b)
, alorsa.hashCode()
doit être le même queb.hashCode()
.
Si vous remplacez l'un, vous devez remplacer l'autre.
Utilisez le même ensemble de champs que vous utilisez pour calculer equals()
pour calculer hashCode()
.
Utilisez les excellentes classes d'assistance EqualsBuilder et HashCodeBuilder de la bibliothèque Apache Commons Lang . Un exemple:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
Lorsque vous utilisez une collection ou une carte de hachage telle que HashSet , LinkedHashSet , HashMap , Hashtable ou WeakHashMap , assurez-vous que le hashCode () des objets clés que vous placez dans la collection ne change jamais tant que l'objet se trouve dans la collection. La manière à l'épreuve des balles de garantir cela est de rendre vos clés immuables, ce qui présente également d'autres avantages .
instanceof
renvoie false si son premier opérande est null (Java effectif à nouveau).Il y a quelques problèmes à noter si vous traitez avec des classes qui persistent avec un mappeur de relation d'objet (ORM) comme Hibernate, si vous ne pensiez pas que c'était déjà déraisonnablement compliqué!
Les objets chargés paresseux sont des sous-classes
Si vos objets sont persistés à l'aide d'un ORM, dans de nombreux cas, vous aurez affaire à des proxys dynamiques pour éviter de charger l'objet trop tôt à partir du magasin de données. Ces proxys sont implémentés en tant que sous-classes de votre propre classe. Cela signifie que
this.getClass() == o.getClass()
cela reviendrafalse
. Par exemple:Si vous avez affaire à un ORM, l'utilisation
o instanceof Person
est la seule chose qui se comportera correctement.Les objets chargés paresseux ont des champs nuls
Les ORM utilisent généralement les getters pour forcer le chargement d'objets chargés paresseux. Cela signifie que ce
person.name
seranull
siperson
est chargé paresseusement, même siperson.getName()
force le chargement et renvoie "John Doe". D'après mon expérience, cela revient plus souvent danshashCode()
etequals()
.Si vous avez affaire à un ORM, assurez-vous de toujours utiliser des getters et de ne jamais mettre en référence des références dans
hashCode()
etequals()
.L'enregistrement d'un objet changera son état
Les objets persistants utilisent souvent un
id
champ pour contenir la clé de l'objet. Ce champ sera automatiquement mis à jour lors de la première sauvegarde d'un objet. N'utilisez pas de champ id danshashCode()
. Mais vous pouvez l'utiliser dansequals()
.Un motif que j'utilise souvent est
Mais vous ne pouvez pas inclure
getId()
danshashCode()
. Si vous le faites, lorsqu'un objet persiste, seshashCode
modifications. Si l'objet est dans unHashSet
, vous ne le retrouverez "plus" jamais.Dans mon
Person
exemple, j'utiliserais probablementgetName()
pourhashCode
etgetId()
plusgetName()
(juste pour la paranoïa) pourequals()
. Ce n'est pas grave s'il y a un risque de "collisions"hashCode()
, mais jamais correctequals()
.hashCode()
doit utiliser le sous-ensemble de propriétés inchangé deequals()
la source
Saving an object will change it's state
!hashCode
doit revenirint
, alors comment allez-vous utilisergetName()
? Pouvez-vous donner un exemple pour votrehashCode
Une clarification sur le
obj.getClass() != getClass()
.Cette déclaration est le résultat d'
equals()
un héritage hostile. Le JLS (spécification du langage Java) spécifie que siA.equals(B) == true
alorsB.equals(A)
doit également retournertrue
. Si vous omettez cette instruction héritant des classes qui remplacentequals()
(et modifient son comportement), cette spécification sera rompue.Prenons l'exemple suivant de ce qui se passe lorsque l'instruction est omise:
Faire
new A(1).equals(new A(1))
aussi, lenew B(1,1).equals(new B(1,1))
résultat donne vrai, comme il se doit.Cela semble très bien, mais regardez ce qui se passe si nous essayons d'utiliser les deux classes:
De toute évidence, c'est faux.
Si vous voulez assurer la condition symétrique. a = b si b = a et le principe de substitution de Liskov appellent
super.equals(other)
non seulement dans le cas de l'B
instance, mais vérifient après parA
exemple:Qui produira:
Où, si ce
a
n'est pas une référence deB
, alors ce pourrait être une référence de classeA
(parce que vous l'étendez), dans ce cas vous appelezsuper.equals()
aussi .la source
ThingWithOptionSetA
peut être égal à a àThing
condition que toutes les options supplémentaires aient des valeurs par défaut, et de même pour aThingWithOptionSetB
, alors il devrait être possible pour aThingWithOptionSetA
d'être comparé égal à aThingWithOptionSetB
seulement si toutes les propriétés non-base des deux objets correspondent à leurs valeurs par défaut, mais Je ne vois pas comment vous testez cela.B b2 = new B(1,99)
, puisb.equals(a) == true
eta.equals(b2) == true
maisb.equals(b2) == false
.Pour une implémentation respectueuse de l'héritage, consultez la solution de Tal Cohen, Comment puis-je implémenter correctement la méthode equals ()?
Sommaire:
Dans son livre Effective Java Programming Language Guide (Addison-Wesley, 2001), Joshua Bloch affirme qu '"il n'y a tout simplement aucun moyen d'étendre une classe instanciable et d'ajouter un aspect tout en préservant le contrat égal". Tal n'est pas d'accord.
Sa solution consiste à implémenter equals () en appelant un autre aveuglément non symétrique Equals () dans les deux sens. blindlyEquals () est écrasé par les sous-classes, equals () est hérité et n'est jamais écrasé.
Exemple:
Notez que equals () doit fonctionner à travers les hiérarchies d'héritage si le principe de substitution de Liskov doit être satisfait.
la source
if (this.getClass() != o.getClass()) return false
, mais flexible en ce qu'il ne renvoie false que si la ou les classes dérivées prennent la peine de modifier égal. Est-ce correct?Toujours étonné qu'aucun ne recommande la bibliothèque de goyave pour cela.
la source
this
authis.getDate()
moyen de rien (autre que le désordre)if (!(otherObject instanceof DateAndPattern)) {
. D'accord avec hernan et Steve Kuo (bien que ce soit une question de préférence personnelle), mais +1 néanmoins.Il existe deux méthodes dans la super classe comme java.lang.Object. Nous devons les remplacer par un objet personnalisé.
Les objets égaux doivent produire le même code de hachage tant qu'ils sont égaux, mais les objets inégaux n'ont pas besoin de produire des codes de hachage distincts.
Si vous voulez en savoir plus, veuillez consulter ce lien sous http://www.javaranch.com/journal/2002/10/equalhash.html
Ceci est un autre exemple, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
S'amuser! @. @
la source
Il y a deux façons de vérifier votre égalité de classe avant de vérifier l'égalité des membres, et je pense que les deux sont utiles dans les bonnes circonstances.
instanceof
opérateur.this.getClass().equals(that.getClass())
.J'utilise # 1 dans une
final
implémentation égale, ou lors de l'implémentation d'une interface qui prescrit un algorithme pour égal (comme lesjava.util
interfaces de collecte - la bonne façon de vérifier avec(obj instanceof Set)
ou quelle que soit l'interface que vous implémentez). C'est généralement un mauvais choix lorsque des égaux peuvent être remplacés car cela rompt la propriété de symétrie.L'option n ° 2 permet d'étendre la classe en toute sécurité sans remplacer les égaux ni rompre la symétrie.
Si votre classe l'est également
Comparable
, les méthodesequals
etcompareTo
doivent également être cohérentes. Voici un modèle pour la méthode equals dans uneComparable
classe:la source
final
et que lacompareTo()
méthode a été remplacée pour inverser l'ordre de tri, les instances de la sous-classe et de la superclasse ne devraient pas être considérées comme égales. Lorsque ces objets étaient utilisés ensemble dans une arborescence, les clés qui étaient «égales» selon uneinstanceof
implémentation pouvaient ne pas être trouvées.Pour des égaux, regardez dans Secrets of Equals par Angelika Langer . Je l'aime beaucoup. Elle est également une excellente FAQ sur les génériques en Java . Voir ses autres articles ici (faites défiler jusqu'à "Core Java"), où elle continue également avec la partie 2 et la "comparaison de types mixtes". Amusez-vous à les lire!
la source
La méthode equals () est utilisée pour déterminer l'égalité de deux objets.
car la valeur int de 10 est toujours égale à 10. Mais cette méthode equals () concerne l'égalité de deux objets. Quand nous disons objet, il aura des propriétés. Pour décider de l'égalité, ces propriétés sont prises en compte. Il n'est pas nécessaire que toutes les propriétés soient prises en compte pour déterminer l'égalité et en ce qui concerne la définition de classe et le contexte, il peut être décidé. Ensuite, la méthode equals () peut être remplacée.
nous devons toujours remplacer la méthode hashCode () chaque fois que nous substituons la méthode equals (). Sinon, que se passera-t-il? Si nous utilisons des tables de hachage dans notre application, elle ne se comportera pas comme prévu. Comme le hashCode est utilisé pour déterminer l'égalité des valeurs stockées, il ne renverra pas la bonne valeur correspondante pour une clé.
L'implémentation par défaut donnée est la méthode hashCode () dans la classe Object utilise l'adresse interne de l'objet et la convertit en entier et la renvoie.
Exemple de sortie de code:
la source
Logiquement, nous avons:
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
Mais pas vice-versa!
la source
Un problème que j'ai trouvé est celui où deux objets contiennent des références l'un à l'autre (un exemple étant une relation parent / enfant avec une méthode pratique sur le parent pour obtenir tous les enfants).
Ce genre de choses est assez courant lors des mappages Hibernate par exemple.
Si vous incluez les deux extrémités de la relation dans votre hashCode ou des tests égaux, il est possible d'entrer dans une boucle récursive qui se termine par une StackOverflowException.
La solution la plus simple consiste à ne pas inclure la collection getChildren dans les méthodes.
la source
equals()
. Si un savant fou créait une copie de moi, nous serions équivalents. Mais nous n'aurions pas le même père.