martes, junio 24, 2008

Equals en java

Mirando las estadísticas del blog (sí, hace tiempo puse un contador de google-analytics, me hace mucha ilusión ver cuantas visitas tienen cada semana) me salen que la mayoría de los que llegan al blog son a través de buscadores, por lo que los pocos que seáis habituales, gracias, no sabéis la ilusión que me hace (y lo que me sube el ego :D ).
El caso es que las visitas más recurrentes van hacia el post de Equals y HashCode y el de java.lang.OutOfMemoryException : PermGem, a parte de que las búsquedas relacionadas con Portlets y Liferay.
¿Porque cuento esto? porque revisando el post me doy cuenta que fue uno de los primeros que escribí, y lo hice más que nada para probar mi javascript de "coloreado de sintaxis" y el CSS y resulta que es ahora uno de los más vistos. Por lo que me siento en parte responsable del tema y he decidido ampliarlo un poco.
Lo primero es hablar del método equals y que significa. A diferencia de otros lenguajes, en Java el símbolo de igualdad (== ) en objetos indica "la misma instancia", no que sus valores sean iguales. Por eso los de Sun inventaron un concepto de "objetos iguales" que no lo mismo que "el mismo objeto". Esta diferencia se hace con el método "equals".
// Aqui tenemos dos clases String
String uno = new String ("Un texto");
String dos = new String ("Un texto");
if (uno == dos ) {// Esta comparacion siempre devuelve false
  ... Esto no se ejecutará
}
if (uno.equals(dos)) {// Esta comparación si devuelve true
 .... Esto si se ejecutará.
}
Por defecto, el objeto padre "Object" tiene implementado el método "equals" como una "igualdad de instancia", por lo que si no sobrescribimos para nuestra clase, "equals" y "==" tienen el mismo resultado.
Para cada caso, el método equals deberá de ser diferente, por ejemplo :
class Punto {
int x;
int y;

// el metodo equals seria :
public boolean equals (Object obj) {
  if (this == obj) {
    return true;
  }
  if (obj instanceof Punto) {
    Punto otro = (Punto)obj;
    return ((x==otro.x) && (y==otro.y))
  } else {
     return false;
  }
}
}
Como se ve ahora dos puntos son iguales si sus coordenadas son las mismas. ¿Se entiende mejor?.
Otro concepto a añadir con el método Equals es el "hashCode". Este método se emplea en las colecciones para poder localizar los elementos dentro de esta. Para ello se incluye en la documentación de Sun una nota :
dos elementos iguales deben de tener el mismo hashCode, siendo esta una propiedad transitiva. Por lo tanto dos objetos con el mismo hashCode no tienen que ser obligatoriamente iguales
De esto se deduce que cuando se sobrescribe el método equals es necesario sobrescribir el método HashCode. Para nuestro ejemplo podríamos hacer:
public int hashCode () {
 // Multiplicamos por numeros primos
 int result = 17;
 result = 37 * result + x;
 result = 37 * result + y;
 return result;
}
Ahora ya tenemos nuestra clase preparada para poder usar el equals correctamente.
Antes de terminar una breve anotación, hacer esta tarea casi siempre es algo pesado y bastante tedioso, por lo que recomiendo usar algunas soluciones más cómodas. La más rápida de encontrar es usar las librerías "commons-lang" de apache. En esta, encontramos unas clases útiles : EqualsBuilder y HashCodeBuilder que permiten "automatizar" el proceso de creación de métodos equals y hashCode.
También existe por ahí algún plugin para eclipse, pero no he sido capaz de volver a encontrarlo.
Publicar un comentario en la entrada