Ho una semplice classe base, che viene successivamente estesa da molte classi separate, che potenzialmente introducono nuovi campi, ma non necessariamente. Ho definito un metodo uguale nella classe base, ma ho anche ignorato quello per poche sottoclassi. Va bene mescolare le definizioni in base/sottoclassi? Nel mio caso è stato per evitare la duplicazione del codice controllando gli stessi campi.Java: uguale al metodo nella classe base e nelle sottoclassi
risposta
Dai un'occhiata a "Implementing equals() To Allow Mixed-Type Comparison" da Angelika Langer.
Ecco una breve spiegazione di alcuni problemi e una possibile soluzione:
Il equivale contratto dice (tra gli altri):
è simmetrico: per eventuali non nulli valori di riferimento x ed y , x.equals (y) dovrebbe restituire true se e solo se y.equals (x) restituisce true.
Questo significa che si potrebbe ottenere problemi se la classe sub sta introducendo nuovi campi e si sta confrontando un oggetto della classe base (o un'altra classe sub che non sovrascrive equals) per un oggetto di questa classe sub .
Non fare il seguente:
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;
}
}
perché si ottiene
BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();
System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'
Una possibile soluzione:
Sostituire la instanceof
-Controllare con un confronto classe:
obj != null && obj.getClass() == getClass()
Con questa soluzione un oggetto di BaseClass
non sarà mai uguale a un oggetto di alcuna sottoclasse.
Se si crea un altro SubClass
senza un @Override
del metodo equals
, due SubClass
-Oggetti possono essere uguali tra loro (se il controllo di BaseClass.equals
decide così), fuori dalla scatola, ma un SubClass
-oggetto non sarà mai uguale a a BaseClass
-oggetto.
una buona implementazione potrebbe essere la seguente:
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;
}
}
Si prega di fare riferimento all'articolo di cui sopra per i problemi più avanzati e le loro soluzioni.
Grazie per il link dell'articolo Angelika Langer. Anche se questo viola il principio di sostituzione di Liskov, AFAIK. Vedi di nuovo "Effective Java" di Joshua Block. (Non ho ancora letto l'intero articolo, però.) – Puce
cosa c'è di sbagliato in o.getClass()! = This.getClass()? – Mukul
@ Mukul Penso che sia davvero meglio perché eviti la necessità di sovrascrivere il metodo equals se non fai controlli addizionali e puoi chiamare 'super.equals()' senza rompere tutto. Ho appena inviato una modifica corrispondente – Qw3ry
Penso che sia perfettamente soddisfacente finché si seguono i contratti eqauls() e hashcode().
è possibile utilizzare il metodo super()
per chiamare il metodo di classe che si sta estendendo a prevenire ogni esigenza di duplicazione del codice
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.....);
}
}
+1 per il consiglio di chiamare super prima. Una nota: non è un codice Java valido, sarebbe bello correggerlo (ho capito che potrebbe essere solo uno pseudo codice come esempio). –
Grazie. L'ho solo messo fuori senza fare controlli sulla maggior parte delle cose. È passato un po 'di tempo da quando lavoro su Java e ho dovuto inserire altre sintassi e chiamate al suo interno. – Grambot
Non si sta eseguendo l'override degli equals di Object, ma piuttosto di sovraccaricarlo di –
Piuttosto un approccio valido. Il problema è in una delle sottoclassi in cui è deve mantenere la definizione di uguale a come è associato dal relativo padre. Altrimenti hai una funzione di equazioni interrotte, che può causare alcuni scenari davvero unici durante il runtime.
No, non è possibile conformarsi al contratto di equals quando si introducono nuovi campi che sono rilevanti per il metodo di uguaglianza. Vedi "Effective Java" di Joshua Bloch per ulteriori informazioni.
Edit:
non ho il libro a portata di mano in questo momento, ma penso che sia ok, se la classe base è astratta/non può essere un'istanza.
immagino, Che è perfetto per fornire l'attuazione equals(Object obj)
e hashCode()
metodo super
class
come Java
fatto. Sappiamo tutti che Java fornisce l'implementazione del metodo hashCode() and equals(Object obj)
nella classe base java.lang.Object e, quando mai richiesto, noi override
nel nostro class
.
Mentre il seguente non gestisce tutti i casi, ho trovato che sia abbastanza pratico. L'ho usato molte volte quando ho in gioco sia una SuperClass che una SubClass. Non voglio compararli tra loro, ma non voglio nemmeno ri-implementare tutti i SuperClass equals() per SubClass. Gestisce:
- a.equals (b) == b.equals (a)
- non duplica codice di confronto campo
- facilmente generalizzato per qualsiasi profondità sottoclasse
- Subclass.equals (SuperClass) == false
- Superclass.equals (SubClass) == false esempio
Codice
// 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()
}
Puoi spiegare cosa intendi mescolando le definizioni. Grazie. – tjg184
Avere una definizione nella classe base, che potrebbe/potrebbe non essere sovrascritta. Intendevo dire che "gli approcci di miscelazione della classe base definisce la sottoclasse definisce" – Bober02