J'ai une classe de base simple, qui est ensuite étendue par de nombreuses classes distinctes, qui introduisent potentiellement de nouveaux champs, mais pas nécessairement. J'ai défini une méthode equals dans la classe de base, mais j'ai également écrasé celle-ci pour quelques sous-classes. Est-il possible de mélanger les définitions dans la base/les sous-classes? Dans mon cas, c'était pour éviter la duplication de code en vérifiant les mêmes champs.
Jetez un coup d'œil à "Implémentation d'equals () pour autoriser la comparaison de types mélangés" de Angelika Langer.
Voici une brève explication de certains problèmes et une solution possible:
Le contrat d'égalité dit (entre autres):
Il est symétrique: pour toute valeur de référence non nulle x et y, x.equals (y) doit renvoyer true si et seulement si y.equals (x) renvoie true.
Cela signifie que vous pourriez avoir des problèmes si votre sous-classe introduit de nouveaux champs et que vous comparez un objet de la classe de base (ou une autre sous-classe qui ne remplace pas égal à égal) à un objet de cette sous-classe.
NE PAS faire ce qui suit:
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BaseClass) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class BadSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BadSubClass) {
return super.equals(obj)
&& field2 == ((BadSubClass) obj).field2;
}
return false;
}
}
parce que tu as
BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();
System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'
Une solution possible:
Remplacez la vérification instanceof
- par une comparaison de classe:
obj != null && obj.getClass() == getClass()
Avec cette solution, un objet de BaseClass
ne sera jamais égal à un objet de toute sous-classe.
Si vous créez une autre variable SubClass
sans le @Override
de la méthode equals
, deux objets SubClass
- peuvent être égaux l'un de l'autre (si la vérification BaseClass.equals
le permet), mais un objet SubClass
- ne sera jamais égal à un objet BaseClass
-variable .
Une bonne implémentation pourrait être la suivante:
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == getClass()) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class GoodSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof GoodSubClass) {
return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
}
return false;
}
}
Veuillez vous référer à l'article mentionné ci-dessus pour des problèmes plus avancés et leurs solutions.
Non, il n'est pas possible de se conformer au contrat d'égalité lors de l'introduction de nouveaux champs pertinents pour la méthode de l'égalité. Voir "Effective Java" de Joshua Bloch pour plus d'informations.
Modifier:
Je n'ai pas le livre sous la main pour le moment, mais je pense que ce n'est pas grave si la classe de base est abstraite/ne peut pas être instanciée.
Je pense que c'est parfaitement bien tant que vous suivez equals () et hashcode () contract.
Bien que ce qui suit ne traite pas tous les cas, je l’ai trouvé assez pratique. J'ai utilisé cela plusieurs fois lorsque j'ai à la fois une SuperClass et une SubClass en jeu. Je ne veux pas les comparer, mais je ne veux pas non plus réimplémenter tous les SuperClass equals () pour SubClass. Il gère:
Exemple de code
// implement both strict and asymmetric equality
class SuperClass {
public int f1;
public boolean looseEquals(Object o) {
if (!(o instanceof SuperClass)) return false;
SuperClass other = (SuperClass)o;
return f1 == other.f1;
}
@Override public boolean equals(Object o) {
return looseEquals(o) && this.getClass() == o.getClass();
}
}
class SubClass extends SuperClass {
public int f2;
@Override public boolean looseEquals(Object o) {
if (!super.looseEquals(o)) return false;
if (!(o instanceof SubClass)) return false;
SubClass other = (SubClass)o;
return f2 == other.f2;
}
// no need to override equals()
}
Vous pouvez utiliser la méthode super()
pour appeler la méthode de la classe que vous étendez afin d'éviter tout besoin de duplication de code.
public class BaseClass {
public boolean equals(BaseClass other) {
return (other.getBlahblah() == this.Blahblah && .....);
}
}
public class DerivedClass extends BaseClass {
public boolean equals(DerivedClass other) {
return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
}
}
Une approche tout à fait valable. Le problème se trouve dans l’une de vos sous-classes il must conserve la définition d’égaux tel qu’il est lié par son parent. Sinon, vous avez une fonction égal à cassé, ce qui peut entraîner des scénarios très uniques au cours de l'exécution.
J'imagine que c'est parfait pour fournir l'implémentation des méthodes equals(Object obj)
et hashCode()
dans super
class
comme Java
l'a fait. Nous savons tous que Java fournit l'implémentation de la méthode hashCode() and equals(Object obj)
dans la classe de base Java.lang.Object , et lorsque cela est nécessaire, nous leur donnons override
dans notre class
name__.