PDF de programación - Clase 9: Igualdad, copia y vistas

Imágen de pdf Clase 9: Igualdad, copia y vistas

Clase 9: Igualdad, copia y vistasgráfica de visualizaciones

Publicado el 6 de Septiembre del 2017
538 visualizaciones desde el 6 de Septiembre del 2017
267,1 KB
14 paginas
Creado hace 20a (12/12/2003)
Clase 9: Igualdad, copia y vistas

9.1 El contrato de la clase Object

Toda clase extiende la clase Object, y por tanto hereda todos sus métodos. Dos de
éstos son especialmente importantes y significativos en todos los programas: el
método para la comprobación de igualdad y el de la generación de código hash.

public boolean equals (Object o)

public int hashCode ()

Como cualquier otro método de una superclase, estos métodos se pueden invalidar.
Veremos en uno de los siguientes temas sobre el subtipado, que una subclase debería ser
un
subtype. Esto significa que debería comportarse según la especificación de la superclase,
de manera que un objeto de la subclase pudiera colocarse en un contexto en el que se
esperase un objeto de la superclase, y aún funcionase adecuadamente.
La especificación de la clase Object es bastante abstracta y puede parecer algo recóndita.
No obstante, la falta de conformidad con ésta puede ocasionar consecuencias nefastas y
tiende a causar errores complejos y oscuros. Y lo que es peor aún, es que si usted no
comprende esta especificación y sus ramificaciones, es probable que su código se vea
impregnado de fallos con un efecto generalizado y difíciles de eliminar sin una compleja
reorganización de todo. La especificación de la clase Object es tan importante que en
muchas ocasiones se denomina “El contrato de Object”.
Se puede encontrar el contrato en las especificaciones de los métodos equals y hashCode,
en la documentación de la API de Java. Se establece que:








equals debe definir una relación de equivalencia, es decir, una relación que
sea reflexiva, simétrica y transitiva;
equals debe ser coherente: las llamadas reiteradas al método deben producir el mismo resultado
a menos que los argumentos sean modificados;

• para una referencia non-null x, x.equals (null) debería devolver el valor false;
• hashCode debe producir el mismo resultado para dos objetos considerados igual por el método

equals;

61

9.2 Propiedades de igualdad
Centrémonos primero en las propiedades del método equals. Reflexividad significa que
un objeto siempre se iguala a sí mismo; simetría significa que cuando a es igual a b, b es
igual a -a; transitividad significa que cuando a es igual a b y b es igual a c, a también es
igual a c.
Puede parecer que estas propiedades son obvias, y de hecho lo son. Si no se cumpliesen,
resultaría complicado imaginar el uso del método equals: tendría que preocuparse de
escribir a.equals(b) o b.equals(a), si por ejemplo, no fuesen simétricos.
Sin embargo, lo que es mucho menos obvio, es la facilidad para romper estas propiedades
inadvertidamente. El siguiente ejemplo, (tomado del excelente libro de Joshua Bloch,
Effective Java: Programming Language Guide, que es además uno de los libros
aconsejados para la asignatura) muestra como la simetría y la transitividad pueden no
cumplirse, cuando utilizamos la herencia.
Considere una clase simple que implementa un punto bidimensional:

public class Point {

private final int x; private
final int y; public Point
(int x, int y) {

this.x = x; this.y = y;
}

public boolean equals (Object o) {

if (!(o instanceof Point))

return false;

Point p = (Point) o;
return p.x == x && p.y == y;
}
… }

Suponga ahora que añadimos la noción de colour:
public class ColourPoint extends Point {

private Colour colour;
public ColourPoint (int x, int y, Colour colour) {

super (x, y);
this.colour = colour;
}


}

¿Cómo debe ser el método equals de la clase ColourPoint? Podríamos heredar únicamente el método equals

62

de Point, pero entonces, los dos objetos ColourPoints se considerarán igual, incluso si
poseen colores diferentes. Podríamos invalidar esto de la siguiente forma:

public boolean equals (Object o) {
if (!(o instanceof ColourPoint))

return false;

ColourPoint cp = (ColourPoint) o;
return super.equals (o) && cp.colour.equals(colour);
}

Este ejemplo aparentemente inofensivo, en realidad viola el requisito de la simetría.
Para comprender la razón de esto, piense en un punto Point y en un punto de color
ColourPoint:

Point p = new Point (1, 2);
ColourPoint cp = new ColourPoint (1, 2, Colour.RED);

Ahora, p.equals(cp) devolverá el valor true, pero ¡cp.equals(p) devolverá false! El
problema es que estas dos expresiones utilizan métodos equals distintos: el primero usa
el método de la clase Point, que obvia el color, y el segundo, el método de la clase
ColourPoint.
Podemos tratar de asegurar esto haciendo que el método equals de la clase ColourPoint
haga caso omiso al color, cuando se dé una comparación con un punto que no sea de
color: public boolean equals (Object o) {

if (!(o instanceof Point))

return false;

//si f o es un Point normal, haga una comparación sin tener en cuenta el color
if (!(o instanceof ColourPoint))

return o.equals (this);

ColourPoint cp = (ColourPoint) o;
return super.equals (o) && cp.colour.equals (colour);
}

Esto soluciona el problema de simetría, ¡pero ahora la igualdad no es transitiva! Para
ver el porqué, tenga en cuenta la construcción de estos puntos:

ColourPoint p1 = new ColourPoint (1, 2, Colour.RED);
Point p2 = new Point (1, 2);
ColourPoint p2 = new ColourPoint (1, 2, Colour.BLUE);

Las llamadas p1.equals(p2) y a p2.equals(p3) devolverán el valor true, excepto
p1.equals(p3), que devolverá false.

63

Parece que no hay solución a este problema: es un problema fundamental de herencia. No se puede escribir un
buen método equals para ColourPoint si esta clase hereda de Point. No obstante, el problema desaparecerá si
usted implementa ColourPoint usando Point en su representación, de forma que un ColourPoint no se trate
más como un Point. Para ver más detalles sobre esto, consulte el libro de Bloch.
En este libro también se facilitan algunos consejos sobre cómo escribir un buen método equals y se observan
algunos problemas típicos. Por ejemplo, ¿qué sucedería si usted escribiese algo como esto y utilizara otro tipo
para Object en la declaración del método equals?

public boolean equals (Point p)

9.3 Hashing
Para comprender la parte del contrato relacionada con el método hashCode, será necesario que tenga una idea
de cómo funcionan las tablas hash.
Las tablas hash son un invento fantástico, una de las mejores ideas dentro del mundo informático. Una tabla
hash es una representación de una asociación: un tipo de datos abstracto que asocia claves con valores. Estas
tablas ofrecen tiempo constante de búsqueda, de modo que tienden a funcionar mejor que los árboles o las
listas. No hace falta que las claves se ordenen o que tengan cualquier propiedad especial, pero sí es necesario
que ofrezcan los métodos equals y hashCode.
Aquí le mostramos cómo funciona una tabla hash. Contiene un array que se inicializa con un tamaño
correspondiente al número de elementos que esperamos que se inserten. Cuando se presentan una clave y un
valor para ser insertados, calculamos el código hash de la clave y lo convertimos en un índice dentro del
intervalo del array (ej.: a través de una divista de módulo). Entonces, se inserta el valor en ese índice.
El invariante Rep de una tabla hash incluye la restricción fundamental de que las claves se encuentran en las
posiciones, determinadas por sus códigos hash. Veremos más tarde por qué esto es importante. Los códigos
hash están diseñados de modo que las claves se distribuyan uniformemente a lo largo de los índices. Sin
embargo, de vez en cuando se da un conflicto y se colocan dos claves en el mismo índice. Así que, en vez de
mantener un único valor en un índice, una tabla hash mantiene de hecho una lista de pares clave/valor
(conocidos normalmente como “hash buckets”), que se implementan en Java como objetos de la clase con dos
campos. Durante la inserción, usted añadirá un par a la lista, en la posición del array determinada por el
código hash. Durante la operación de búsqueda, aplicará una función hash a la clave, encontrará la posición
correcta del array, y luego examinará cada uno de los pares hasta que encuentre uno cuya clave se
corresponda con la clave determinada.
Ahora, debería haberle quedado claro por qué el contrato de la clase Object exige que objetos iguales posean
la misma clave hash.

64

Si dos objetos iguales poseen claves hash distintas, éstos podrían colocarse en posiciones diferentes. Así que si
intenta buscar un valor usando una clave igual a la que utilizó cuando éste se insertó, es posible que la
búsqueda falle.
Una manera sencilla y notoria de asegurar que el método hashCode satisface el contrato del objeto, es hacer
que este método devuelva siempre algún valor constante, de manera que el código hash de cada objeto sea el
mismo. Esto satisface el contrato de Object, pero tendría un resultado de funcionamiento desastroso, ya que
cada clave se almacenará en la misma posición y cada búsqueda degenerará en una búsqueda lineal a lo largo
de una lista larga.
El modo estándar para construir un código hash más razonable que aún satisfaga el contrato, consistiría en
calcular un código hash para cada componente del objeto que se ha usado en la determinación de la igualdad
(normalmente llamando al método hashCode de cada componente) y en combinar luego cada código
conseguido, a través de unas operaciones aritméticas. Consulte el libro de Bloch para más detalles.
Y lo más importante es que debe observar que, si usted no invalida el método hashCode, obtendrá el de la clase
Object, que está basado en la dirección del objeto. Si usted ha invalidado el método equals, casi seguro que
habrá violado el contrato. Por tanto, como regla general:

Invalide siempre el método hashCode cuando invalide el método equals.

(Éste es uno de los aforismos de Bloch. El libro al completo es una colección de aforismos como éste, bien
explicados e ilustrados).
El pasado año, un estudiante pasó
  • Links de descarga
http://lwp-l.com/pdf6806

Comentarios de: Clase 9: Igualdad, copia y vistas (0)


No hay comentarios
 

Comentar...

Nombre
Correo (no se visualiza en la web)
Valoración
Comentarios...
CerrarCerrar
CerrarCerrar
Cerrar

Tienes que ser un usuario registrado para poder insertar imágenes, archivos y/o videos.

Puedes registrarte o validarte desde aquí.

Codigo
Negrita
Subrayado
Tachado
Cursiva
Insertar enlace
Imagen externa
Emoticon
Tabular
Centrar
Titulo
Linea
Disminuir
Aumentar
Vista preliminar
sonreir
dientes
lengua
guiño
enfadado
confundido
llorar
avergonzado
sorprendido
triste
sol
estrella
jarra
camara
taza de cafe
email
beso
bombilla
amor
mal
bien
Es necesario revisar y aceptar las políticas de privacidad