java - ¿Qué parte del contrato general es igual a() igual a mi igual?
eclipse equals (3)
Soy bastante nuevo en java y solo estoy tratando de entender @Override
el modo @Override
of the equals()
y hashcode()
.
Sé que para que el método equals sea correcto, debe ser:
- Reflexivo:
a.equals(a)
- Simétrico:
a.equals(b)
luegob.equals(a)
- Transitivo:
a.equals(b) && b.equals(c)
Luegoa.equals(c)
- No nulo
! a.equals(null)
! a.equals(null)
Estoy luchando para identificar cuál de las propiedades anteriores soy y no estoy satisfaciendo cuando escribo mi overide del método equals.
Soy consciente de que Eclipse puede generarlos para mí, sin embargo, como todavía no he recibido el concepto por completo, escribirlo me ayuda a aprender.
He escrito lo que creo que es la forma correcta de hacerlo, pero cuando verifico con la versión generada por Eclipse, parece que "faltan" algunos aspectos.
Ejemplo:
public class People {
private Name first; //Invariants --> !Null, !=last
private Name last; // !Null, !=first
private int age; // !Null, ! <=0
...
}
Lo que escribí:
public boolean equals(Object obj){
if (obj == null){
return false;
}
if (!(obj instanceof People)){
return false;
}
People other = (People) obj;
if (this.age != other.age){
return false;
}
if (! this.first.equals(other.first)){
return false;
}
if (! this.last.equals(other.last)){
return false;
}
return true;
}
eclipse vs generado
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People other = (People) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (age != other.age)
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
Estoy perdido:
if (this == obj) return true;
if (getClass() != obj.getClass()) return false;
Y para cada variable:
if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false;
No estoy seguro de qué es getClass()
y mi implementación es incorrecta.
No creo que su implementación sea incorrecta, pero algunas notas:
if (this == obj) return true;
Es una optimización del rendimiento, prueba directamente la igualdad de referencia y las pruebas de cortocircuito donde a
es a
.
if (getClass() != obj.getClass()) return false;
Es similar a su instanceof
llamada, optimiza lejos un cheque nulo. Las otras llamadas parecen ser cheques nulos.
No necesitas escribir
if (obj == null){
return false;
}
if (!(obj instanceof People)){
return false;
}
Porque null
siempre da false
en el instanceof
cheques. Así que estas líneas se pueden simplificar a solo
if (!(obj instanceof People)){
return false;
}
En cuanto a su pregunta principal de si su método cumple con los requisitos para un método equals()
, estrictamente hablando, la respuesta es no, o al menos es potencialmente poco fiable. Esto se debe a que sería posible extender la clase de la siguiente manera
public class SpecialPeople extends People {
// code omitted
@Override
public boolean equals(Object object) {
if (object == null || object.getClass() != getClass())
return false;
SpecialPeople other = (SpecialPeople) object;
return other.getAge() == getAge()
&& other.getFirst().equals(getFirst())
&& other.getLast().equals(getLast());
}
Ahora suponga que a
es una instancia de People
y b
es una instancia de SpecialPeople
. Supongamos también que b
tienen el mismo nombre y edad. Entonces
a.equals(b) == true // instanceof check succeeds
b.equals(a) == false // getClass() check fails
Por lo tanto equals()
no es simétrico! Por esta razón, si está utilizando instanceof
lugar de getClass()
en equals()
, probablemente debería hacer que el método equals()
final
o la clase final
.
Primera pieza de código:
if (this == obj)
return true;
Esto mejora el rendimiento en caso de que compares la referencia del objeto contra sí mismo. Ejemplo: a.equals(a);
.
Segunda pieza de código:
if (getClass() != obj.getClass())
return false;
Esto compara si la clase de la referencia que se está comparando es la misma clase de this
. La diferencia entre usar este enfoque y instanceof
es que es más restrictivo cuando se compara con una subclase. Ejemplo:
public class Foo { }
public class Bar extends Foo { }
//...
Foo foo = new Bar();
System.out.println(foo instanceof Bar); //prints true
System.out.println(foo instanceof Foo); //prints true
Foo foo2 = new Foo();
System.out.println(foo.getClass() == foo2.getClass()); //prints false
¿Cuál debería elegir? No hay un enfoque bueno o malo, dependerá de su diseño deseado.
Tercera pieza de código:
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false; //For each variable.
Esto es simplemente una comprobación nula para cada campo de referencia de objeto en la clase. Tenga en cuenta que si this.first
es null
, hacer this.first.equals(...)
generará una NullPointerException
.