Вы находитесь на странице: 1из 18

FUNDAMENTOS DE PROGRAMACIN

Versin: 0.0.9

Tema 3. Diseo de Tipos I


Autor: Miguel Toro Bonilla
Revisin: Jos C. Riquelme
Adaptacin a FP: Rafael Romero, Antonia M. Reina
Tiempo estimado: 4 horas
1.

Esquema de diseo de tipos ....................................................................................................................... 1

2.

Casos de prueba .......................................................................................................................................... 2

3.

Implementacin de casos de prueba ......................................................................................................... 5

4.

Recomendaciones para definir casos de prueba. ...................................................................................... 8

5.

El tipo Object ............................................................................................................................................... 9

6.

El tipo Comparable.................................................................................................................................... 12

7.

El tipo Comparator .................................................................................................................................... 14

8.

Ordenacin de arrays con la clase de utilidad Arrays. ............................................................................ 16

Ejercicios............................................................................................................................................................ 18

1. Esquema de diseo de tipos


En este captulo vamos a ampliar el esquema de diseo de tipos que vimos en el Tema 1. Este esquema ser
ampliado en temas posteriores. Es decir, iterativamente vamos a ir completando nuestro esquema de diseo
conforme vayamos avanzando en los temas de la asignatura.
Un buen diseo de tipos es bsico para que los programas sean comprensibles y fciles de mantener. Veamos
algunas pautas para este diseo y algunos ejemplos que puedan servir de gua.
En el Tema 1, vimos que debamos fijarnos en las propiedades del tipo, en el tipo de cada una, en si son
consultables y modificables, o si son bsicas o derivadas. Adems de esto, al disear un tipo nuevo debemos
partir de los ya existentes. Es necesario decidir a qu otros tipos extender. En este captulo nos vamos a centrar
en los tipos Object y Comparable, y aunque se pueden extender otros tipos, como hemos visto en el Tema 2,
la extensin de los mismos se ver con ms detalle en temas posteriores.
La primera decisin a tomar es qu tipos debe extender el tipo diseado o qu otros tipos puede usar. Para
ello es conveniente disponer de un catlogo de tipos tiles para disear otros tipos. Teniendo esto en cuenta,
nuestra plantilla para el diseo de un tipo, de momento, queda de la siguiente forma:
NombreDeTipo extiende T1, T2

El tipo es mutable o inmutable


Propiedades
o NombreDePropiedad, Tipo, Consultable o no, derivada o no. Un posible comentario.
Restricciones.

Introduccin a la Programacin

o
Operaciones

o
Criterio de Igualdad: detalles
Representacin como cadena: detalles
Orden natural: si lo tiene especificar detalles
Orden alternativo: si lo tiene, especificar detalles

En el esquema anterior el criterio de igualdad y la representacin como cadena estn relacionados con el tipo
Object, mientras que el orden natural est relacionado con el tipo Comparable, y el orden alternativo con el
tipo Comparator. Estos tipos los veremos con ms detalles en los siguientes apartados.
2. Casos de prueba
Ya hemos visto que al disear un tipo nuevo debemos escribir una interfaz. Adems de la interfaz, el diseo
del tipo se debe acompaar de un conjunto de casos de prueba que indicarn la forma en que debe
comportarse la implementacin. O dicho de otra forma, los valores esperados (o excepciones esperadas)
cuando invocamos a un mtodo con determinados parmetros o cuando llamamos a un constructor. Estos
casos de prueba los presentaremos en una tabla.
Los casos de prueba se piensan siempre para una clase dada que implementa un tipo. En esta seccin
utilizaremos la clase la clase PersonaImpl y el tipo Persona para guiar la explicacin de los casos de prueba. El
tipo Persona tiene la siguiente descripcin.
Tipo Persona
Propiedades:
DNI: String. Bsica. Cons/Mod
nombre: String. Bsica. Cons/Mod
apellidos: String. Bsica. Cons/Mod
fecha nacimiento: LocalDate. Bsica. Cons/Mod
email: String. Bsica. Cons/Mod
edad: Integer. Derivada.
Representacin como cadena:
DNI, seguido de un guin, los apellidos, una coma, el nombre, otro guin y la fecha de nacimiento.
Ej: 28864657W Garca Vaquero, Pascual 15/09/1998
Constructores:
C1: Uno con un parmetro por cada propiedad bsica.
C2: Otro que con todas las propiedades bsicas menos el email. El valor por defecto para el email
es la cadena vaca
Restricciones:

2. Elementos del lenguaje Java

R1: El DNI de una persona debe estar formado por 8 dgitos y una letra, que se calcula mediante
una frmula conocida. En caso de no cumplirse se elevar ExcepcionPersonaNoValida.
R2: El email debe contener el carcter @, o bien ser la cadena vaca si la persona an no ha
comunicado ese dato. En caso de no cumplirse se elevar ExcepcionPersonaNoValida.

El conjunto de casos de prueba se presenta como una tabla. Cada caso de prueba da lugar a un test. Es decir,
una prueba del funcionamiento adecuado del mtodo en un caso concreto. Para cada uno de los mtodos de
la interfaz se debe definir un conjunto de casos de prueba. Adems, se deben definir casos de prueba para los
constructores, y para los mtodos de la clase Object redefinidos en la clase de implementacin.
El conjunto de casos de prueba para un mtodo debe capturar lo mejor posible la casustica de utilizacin del
mtodo. Los casos de prueba incluirn tanto ejemplos para los que se espera que el mtodo funcione
realizando la operacin correctamente (modo de funcionamiento normal), como ejemplos deducidos de las
restricciones del tipo, que provoquen que el mtodo eleve una excepcin (modo excepcional).
As, para cada mtodo definiremos una tabla, en la que se indiquen los valores de los parmetros, el valor que
se espera que devuelva el mtodo, y la excepcin que se eleva, en caso de funcionamiento excepcional.
De esta forma, podramos pensar en los casos de prueba siguientes para el constructor C1:
1. Creacin de una persona con datos correctos (comportamiento normal).
2. Creacin de una persona con el dni incorrecto (comportamiento excepcional):
a. El dni tiene el nmero de caracteres esperado, pero el ltimo no es una letra.
b. El dni tiene un nmero de caracteres menor del esperado.
c. El dni tiene un nmero de caracteres mayor del esperado.
d. El dni tiene el nmero de caracteres esperado, el ltimo es una letra, pero la letra del dni no
se corresponde con los dgitos.
3. Creacin de una persona con el correo electrnico incorrecto (comportamiento excepcional):
a. El correo electrnico no tiene arroba.
b. El correo electrnico es nulo.
Fjese que los casos de prueba 1 y 2 sirven para comprobar el funcionamiento en modo normal del
constructor, y los casos del 3 al 7, el funcionamiento en modo excepcional. Adems, los casos 3, 4, 5 y 6 estn
relacionados con la restriccin R1 sobre el DNI, y los casos 7 y 8 con la restriccin R2 sobre el email. Teniendo
en cuenta estos casos de prueba, una tabla asociada a los mismos es la siguiente:
nombre

apellidos

DNI

Fecha
nacimiento

Correo electrnico

Excepcin

Juan

12345678Z

15/03/1950

juan.nadie@gmail.com

2
3

Mara
Juan

12345677J
123456789

13/07/1980
15/03/1950

"" (cadena vaca)


juan.nadie@gmail.com

Mara

Nadie
Nadie
Gil Gil
Nadie
Nadie
Gil Gil

1234567X

13/07/1980

margil@hotmail.com

Mara

Gil Gil

123456789X

13/07/1980

margil@hotmail.com

ExcepcionPersona
NoValida
ExcepcionPersona
NoValida
ExcepcionPersona
NoValida

Introduccin a la Programacin

Juan

Mara

Juan

Nadie
Nadie
Gil Gil

12345678A

15/03/1950

juan.nadie@gmail.com

12345677J

13/07/1980

margil

Nadie
12345678Z
Nadie
Tabla 1. Casos de prueba para el constructor C1.

12/07/1999

null

ExcepcionPersona
NoValida
ExcepcionPersona
NoValida
ExcepcionPersona
NoValida

Cuando definamos casos de prueba para un mtodo que pueda modificar al objeto que lo invoca, en la tabla
deberan aparecer columnas para mostrar el estado del objeto antes del invocar al mtodo, y para mostrar el
estado del objeto despus de invocarlo. Un mtodo de este tipo es el mtodo setEmail, que puede modificar
la propiedad email del objeto que lo invoca.
Al igual que ocurra con el constructor, habra que definir casos de prueba para testear el mtodo en su modo
normal de funcionamiento y en su modo excepcional. Para el mtodo setEmail, definiremos casos de prueba
para:
1. Cambiar el email por un email que contenga una arroba (comportamiento normal).
2. Cambiar el email por un email que sea la cadena vaca (comportamiento normal).
3. Cambiar el email por un email que no contiene una arroba (comportamiento excepcional).
4. Cambiar el email por un email que sea nulo (comportamiento excepcional).
Fjese que en este caso la tabla de casos de prueba tiene una columna llamada this, que representa el estado
del objeto antes de invocar al mtodo setEmail, y otra columna llamada this, que representa el estado
esperado del objeto despus de invocar al mtodo. La columna email se refiere al parmetro del mtodo
setEmail.
This
1

Email

<Juan,
juan@us.es
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>
2 <Juan,
"" (cadena vaca)
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>
3 <Juan,
jgmail.com
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>
4 <Juan,
Null
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>
Tabla 2. Casos de prueba para el mtodo setEmail

this

Excepcin

<Juan,
Nadie Nadie,
12345678Z,
15/03/1950,
juan@us.es>
<Juan,
Nadie Nadie,
12345678Z,
15/03/1950,
"">
<Juan,
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>
<Juan,
Nadie Nadie,
12345678Z,
15/03/1950,
@gmail.com>

ExcepcionPersonaNoValida

ExcepcionPersonaNoValida

Un ltimo tipo de mtodo es aqul que no modifica el objeto que lo invoca, sino que consulta una o varias
propiedades del mismo, y devuelve un valor concreto. En este caso, la tabla de casos de prueba deber tener
una columna que refleje el valor que se espera que devuelva el mtodo, y al igual que en los casos anteriores,
habr que buscar ejemplos para el modo de funcionamiento normal y el modo de funcionamiento excepcional.

2. Elementos del lenguaje Java

Si definimos casos de prueba para el mtodo getEdad, tendremos que tener en cuenta ejemplos que cubran
los siguientes casos:
1. Una persona con fecha de nacimiento de un ao anterior al ao en curso y un mes anterior al mes en
curso (funcionamiento normal).
2. Una persona con fecha de nacimiento de un ao anterior al ao en curso y un mes posterior al mes
en curso (funcionamiento normal).
3. Una persona con fecha de nacimiento del ao en curso y un mes anterior al mes en curso
(funcionamiento normal).
4. Una persona con fecha de nacimiento del ao en curso y un mes posterior al mes en curso
(funcionamiento normal).
5. Una persona con fecha de nacimiento de un ao posterior al ao en curso (funcionamiento normal).
Suponiendo, que la fecha en curso es el 05/10/2015, una tabla con casos de prueba para este mtodo es la
siguiente:
This
1 <Juan, Nadie Nadie, 12345678Z,
2 <Juan, Nadie Nadie, 12345678Z,
3 <Juan, Nadie Nadie, 12345678Z,
4 <Juan, Nadie Nadie, 12345678Z,
5 <Juan, Nadie Nadie, 12345678Z,
Tabla 3. Casos de prueba para getEdad

15/03/1950,@gmail.com>
15/11/1950,@gmail.com>
15/03/2015,@gmail.com>
15/11/2015,@gmail.com>
15/11/2018,@gmail.com>

Resultado

Excepcin

65
64
0
0
0

Note que no hay casos de prueba para el modo de funcionamiento excepcional, ya que no se espera que el
mtodo getEdad eleve una excepcin, es decir, siempre debe devolver un valor.
3. Implementacin de casos de prueba
Para decidir si la implementacin que hacemos de un mtodo pasa los casos de prueba diseados podemos
usar la herramienta jUnit. Otra alternativa es Implementar una clase TestTipo (para el tipo Persona,
TestPersona) que haga un trabajo equivalente a la herramienta jUnit o muestre los resultados esperados en
cada uno de los casos de prueba.
Por cada mtodo, definiremos un mtodo de test con visibilidad privada. As, la clase TestPersona tendr los
mtodos testConstructorC1, testSetEmail y testGetEdad, cuyo cdigo se muestra en los listados Listado 1,
Listado 2 y Listado 3. Cada uno de estos mtodos ser invocado tantas veces como casos de prueba se hayan
definido para el mtodo a probar.
En el caso del constructor, el mtodo de prueba se encarga de crear el objeto y mostrar todas las propiedades
observables. Se deben capturar todas las excepciones que el mtodo pueda elevar en su comportamiento
excepcional (en el caso del constructor, se captura ExcepcionPersonaNoValida). Finalmente, se debe capturar,
de forma genrica, cualquier otra excepcin que se pueda elevar, para informar de que el mtodo no se
comporta como se espera (eso se consigue capturando Exception).
private static void testConstructorC1(String nombre, String apellidos,
String dni, LocalDate fechaNacimiento, String email) {
try {
Persona p = new PersonaImpl(nombre, apellidos, dni,

Introduccin a la Programacin

fechaNacimiento, email);
mostrarPersona(p);
} catch (ExcepcionPersonaNoValida e) {
System.out.println("******************** Se ha capturado la
excepcin ExcepcionPersonaNoValida");
} catch (Exception e) {
System.out.println("******************** Se ha capturado una excepcin
inesperada. El constructor no funciona correctamente");
}
}
Listado 1. Mtodo de test para el constructor.

En el caso un mtodo que modifique al objeto que lo invoca, el mtodo de prueba se encarga de: (1) mostrar
la propiedad o propiedades del objeto que se ven modificadas por el mtodo antes de invocar al mismo; (2)
invocar al mtodo que se est probando; y (3) mostrar de nuevo la propiedad o propiedades que el mtodo
debe cambiar. Se deben capturar todas las excepciones que el mtodo pueda elevar en su comportamiento
excepcional (en el caso de setEmail, se captura ExcepcionPersonaNoValida). Finalmente, se debe capturar, de
forma genrica, cualquier otra excepcin que se pueda elevar, para informar de que el mtodo no se comporta
como se espera (eso se consigue capturando Exception).
private static void testSetEmail(Persona p, String nuevoEmail) {
try {
System.out.println("El email antes de la operacin es: "
+ p.getEmail());
System.out.println("El nuevo email es: " + nuevoEmail);
p.setEmail(nuevoEmail);
System.out.println("El email despus de la operacin es: "
+ p.getEmail());
} catch (ExcepcionPersonaNoValida e) {
System.out.println("******************** Se ha capturado la excepcin
ExcepcionPersonaNoValida");
} catch (Exception e) {
System.out.println("******************** Se ha capturado
una excepcin inesperada. setEmail no funciona correctamente");
}
}
Listado 2. Mtodo de test para setEmail

En el caso un mtodo que no modifique al objeto que lo invoca, el mtodo de prueba se encarga de: (1)
invocar al mtodo que se est probando; y (2) mostrar el resultado que devuelve el mtodo. Se deben capturar
todas las excepciones que el mtodo pueda elevar en su comportamiento excepcional (en el caso de getEdad,
no se captura ninguna, ya que se espera que el mtodo funcione siempre en modo normal). Finalmente, se
debe capturar, de forma genrica, cualquier otra excepcin que se pueda elevar, para informar de que el
mtodo no se comporta como se espera (eso se consigue capturando Exception).
private static void testGetEdad(Persona p) {
try {
System.out.println("La edad es: " + p.getEdad());
} catch (Exception e) {
System.out.println("******************** Se ha capturado
una excepcin inesperada. getEdad no funciona correctamente");
}
}
Listado 3. Mtodo de test para getEdad

Una vez definidos estos mtodos, habr que invocarlos tantas veces como casos de prueba se hayan definido
para probar el mtodo. As, el mtodo principal de la clase de test se reducir a invocaciones a mtodos que

2. Elementos del lenguaje Java

ejecuten los casos de prueba. Recuerde que para el constructor se haban definido siete casos de prueba, por
lo que en el main habr invocaciones a testConstructorC1_CP1, que se encargar de ejecutar el primer caso
de prueba; testConstructorC1_CP2 se encargar de ejecutar el segundo caso de prueba, y as, sucesivamente.
public class TestPersona {
public static void main(String[] args) {
testConstructorC1_CP1();
testConstructorC1_CP2();
testConstructorC1_CP3();
testConstructorC1_CP4();
testConstructorC1_CP5();
testConstructorC1_CP6();
testConstructorC1_CP7();
testConstructorC1_CP8();
testSetEmail_CP1();
testSetEmail_CP2();
testSetEmail_CP3();
testSetEmail_CP4();
testSetEmail_CP5();
testGetEdad_CP1();
testGetEdad_CP2();
testGetEdad_CP3();
testGetEdad_CP4();
}
}
Listado 4. Mtodo principal de TestPersona

Finalmente, veamos los mtodos que se encargan de ejecutar los casos de prueba. Los Listados 5, 6 y 7
muestran el aspecto de estos mtodos. Note que estos mtodos nicamente se encargan de crear los datos
de entrada necesarios para probar los mtodos (no olvide que estos datos estn tomados de las
correspondientes tablas con los casos de prueba) e invocar al mtodo que se encarga de ejecutar la prueba.
private static void testConstructorC1_CP1() {
System.out.println("==================================
constructorC1: Caso de prueba 1");
testConstructorC1("Juan", "Nadie Nadie", "12345678Z",
LocalDate.of(1950, 3, 15), "juan.nadie@gmail.com");
}
Listado 5. Mtodo para ejecutar el primer caso de prueba del constructor C1
private static void testSetEmail_CP1() {
System.out
.println("================================== setEmail: Caso de prueba 1");
Persona p = new PersonaImpl("Juan", "Nadie Nadie", "12345678Z",
LocalDate.of(1950, 3, 15), "@gmail.com");
testSetEmail(p, "juan@us.es");
}
Listado 6. Mtodo para ejecutar el primer caso de prueba de setEmail
private static void testGetEdad_CP1() {
System.out
.println("================================== getEdad: Caso de prueba 1");
Persona p = new PersonaImpl("Juan", "Nadie Nadie", "12345678Z",
LocalDate.of(1950, 3, 15), "@gmail.com");
testGetEdad(p);
}
Listado 7. Mtodo para ejecutar el primer caso de prueba de getEdad.

Introduccin a la Programacin

4. Recomendaciones para definir casos de prueba.


Cuando diseamos casos de prueba se debe ser lo ms exhaustivo posible, para asegurar que nuestros
mtodos operan tal y como se espera. En esta seccin se dan algunas guas para definir los casos de prueba.
Normalmente las restricciones de los tipos y las precondiciones de los mtodos se especifican mediante
expresiones con operadores lgicos. En el caso en que tengamos que disear casos de prueba para este tipo
de expresiones, tendremos que cubrir todas las posibilidades para las expresiones bsicas.
Recomendacin 1: La restriccin o precondicin es una expresin relacional simple.
Si lo que tenemos es una expresin relacional simple como x > 0, habr que desarrollar, al menos, tres casos
de prueba, uno para el valor lmite (x = 0), otro para un valor de x mayor que cero, y otro para un valor de x
menor que cero. Un ejemplo de este tipo de restriccin la tenemos en el tipo Circulo, en el que solo se
permiten valores positivos en la propiedad radio.
A la hora de disear casos de prueba para el mtodo setRadio, habr que definir, al menos tres casos de
prueba, uno que tome un valor para el radio mayor que cero, otro que tome un valor para el radio menor que
cero y otro que tome un valor para el radio igual a cero. As una tabla con estos casos de prueba para el mtodo
setRadio sera.

1
2
3

this

nuevoRadio

this

Excepcin

<<1.5, 2.3>, 3.0>


<<1.5, 2.3>, 3.0>
<<1.5, 2.3>, 3.0>

3.3
0.0
-100.44

<<1.5, 2.3>, 3.3>


<<1.5, 2.3>, 3.0>
<<1.5, 2.3>, 3.0>

ExcepcionCirculoNoValido
ExcepcionCirculoNoValido

En los casos en que una parte de la expresin es una constante y la otra una variable el lmite est claro, es la
constante; en los casos en que las dos partes de la expresin son variables es necesario fijar una como si fuera
la constante y la otra como variable. Por ejemplo, si tenemos la expresin x > y con x e y de tipo entero,
podramos hacer una prueba con x = y, otra con x = y +1 y otra con x = y 1. O tambin una prueba con y = x,
otra con y = x +1 y otra con y = x 1.
Gua 2: La restriccin o precondicin es una expresin con operadores lgico.
Si se tiene una precondicin o restriccin del estilo x > 0 && y > 0 entonces generaremos casos de prueba que
cubran todas las opciones, para ello definiremos la tabla de verdad, de la siguiente forma:
x > 0
T
T
F
F

y > 0
T
F
T
F

A partir de esta tabla de verdad, vemos que tenemos que desarrollar al menos un caso de prueba positivo en
el que los dos predicados se cumplan y, al menos, tres casos de prueba negativos en los que alguno de los
predicados o varios a la vez son falsos.

2. Elementos del lenguaje Java

Gua 3: La restriccin o precondicin contiene variables de tipo coleccin.


En el caso de variables de tipo coleccin (lo que incluye las variables de tipo String) debemos hacer al menos
una prueba en la que la coleccin tiene cero elementos, un elemento y varios elementos.
Gua 4: La restriccin o precondicin contiene variables de un tipo definido por el usuario.
En el caso de variables que sean de algn tipo definido por el usuario es conveniente hacer una prueba
especfica para el caso de que la variable tome el valor null.
5. El tipo Object
En Java existe una clase especial llamada Object. Todas las clases que definamos y las ya definidas heredan de
Object, es decir, implcitamente Object es un tipo al que extienden todas las clases Java. Como cualquier tipo,
Object proporciona una serie de mtodos pblicos, no static. Aunque tiene ms mtodos, en este Tema nos
vamos a centrar solamente en tres de esos mtodos: equals, hashCode y toString. Veremos sus propiedades,
las restricciones entre ellos y la forma de redisearlos para que se ajusten a nuestras necesidades. La signatura
de estos mtodos es1:
boolean equals(Object o);
int hashCode();
String toString();
Como el tipo Object es ofrecido por todos los objetos que creemos, los mtodos anteriores estn disponibles
en todos los objetos, es decir, todos los objetos heredan los mtodos equals, hashCode y toString de la clase
Object. Estos mtodos tienen definido un comportamiento por defecto. Veamos para qu se utilizan cada uno
de estos mtodos:

El mtodo equals(Object o) se utiliza para decidir si el objeto es igual al que se le pasa como parmetro.
Recordemos que para decidir si dos objetos son idnticos se usa el operador ==.
El mtodo hashCode() devuelve un entero, que es el cdigo hash del objeto. Todo objeto tiene, por lo
tanto, un cdigo hash asociado.
El mtodo toString() devuelve una cadena de texto que es la representacin exterior del objeto. Cuando
el objeto se muestre en la consola tendr el formato indicado por su mtodo toString correspondiente.

Todos los objetos ofrecen estos tres mtodos. Por lo tanto es necesaria una buena comprensin de sus
propiedades y un buen diseo de los mismos.
Propiedades y restricciones.
Propiedades de equals:

Puede ver el resto de mtodos de Object en la documentacin de la API de Java:


https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html

10

Introduccin a la Programacin

Reflexiva: Un objeto es igual a s mismo. Es decir, para cualquier objeto x, distinto de null, se debe cumplir
que x.equals(x) es true y x.equals(null) es false.
Simtrica: Si un objeto es igual a otro, el segundo tambin es igual al primero. Es decir, para dos objetos
cualesquiera x e y, distintos de null, se debe cumplir que x.equals(y) => y.equals(x). Mltiples invocaciones
de x.equals(y) deben devolver el mismo resultado si el estado de x e y no ha cambiado.
Transitiva: Si un objeto es igual a otro, y este segundo es igual a un tercero, el primero tambin ser igual
al tercero. Es decir para tres objetos x, y, z, distintos de null, se debe cumplir que x.equals(y) && y.equals(z)
=> x.equals(z).

Propiedades de equals/toString:

Si dos objetos son iguales, sus representaciones en forma de cadena tambin deben serlo. Es decir, para
dos objetos cualsequiera x e y, distintos de null, se debe cumplir que x.equals(y) =>
x.toString().equals(y.toString()).

Propiedades de equals/hashCode:

Si dos objetos son iguales, sus cdigos hash tienen que coincidir. La inversa no tiene por qu ser cierta. Es
decir, para dos objetos cualesquiera x e y, distintos de null, se debe cumplir que x.equals(y) =>
x.hashCode() == y.hashCode(). Sin embargo, no se exige que dos objetos no iguales produzcan cdigos
hash desiguales, aunque hay que ser consciente de que se puede ganar mucho en eficiencia si en la
mayora de los casos objetos distintos tienen cdigos hash distintos.

Las propiedades y restricciones anteriores son muy importantes. Si no se cumplen, el programa que diseemos
puede que no funcione adecuadamente. Todos los tipos ofrecidos en la API de Java tienen un diseo adecuado
de esos tres mtodos. Pero recordemos que esos tres mtodos estn slo disponibles en los tipos que
extienden Object. Es decir, en los tipos que definen objetos y no estn disponibles en los tipos primitivos. Por
lo tanto, esos mtodos ya estn disponibles en Integer, Long, Float, Double y String, pero no en los tipos int,
long, float y double.
Veamos un ejemplo con la implementacin en el tipo Punto del tema 1, el esquema de diseo del tipo, ahora
sera:
Tipo Punto
Propiedades:
o Coordenada X: Double, consultable y modificable.
o Coordenada Y: Double, consultable y modificable.
Otras operaciones:
o distanciaAOtroPunto (Double p): Double, devuelve la distancia del punto que invoca al mtodo al
punto p.
Representacin como cadena: (coordenadaX, coordenadaY)
Criterio de igualdad: Dos puntos son iguales si sus coordenadas X son iguales y sus coordenadas Y son
iguales.

2. Elementos del lenguaje Java

Teniendo la descripcin anterior del tipo Punto, los mtodos toString, equals y hashCode que deberan
incluirse en la clase PuntoImpl son los siguientes:
Ejemplo 1 : Mtodos toString, equals y hashCode para el tipo Punto

public String toString() {


String s;
s = "(" + getX() + ", " + getY() + ")";
return s;
}
public boolean equals(Object o) {
boolean r = false;
if (o instanceof Punto) {
Punto p = (Punto) o;
r = this.getX().equals(p.getX()) && this.getY().equals(p.getY());
}
return r;
}
public int hashCode() {
return getX().hashCode() * 31 + getY().hashCode();
}

Veamos con un poco de ms detalle el cdigo anterior, del que comentaremos algunos detalles:

En relacin a la implementacin de equals :


o

En relacin a la implementacin de toString:


o

Podemos estar seguros que el casting a Punto no lanzar excepciones puesto que se hace despus
de comprobar que objeto ofrece ese tipo. Evidentemente si p no es de tipo Punto el resultado es
false.
Hemos supuesto que sus propiedades no pueden tomar el valor null. Si esto no fuera as, se
lanzara una excepcin en getX().equals(p1.getX()) puesto que se intentara invocar el
mtodo equals sobre un objeto null. Eclipse proporciona de forma automtica un cdigo ms
completo que tiene en cuenta la posibilidad de que el objeto a comparar, o algunos de los
atributos que participan en la igualdad, sean null.

La cadena resultante en el mtodo toString se debe calcular a partir de los resultados devueltos
por los correspondientes toString de las propiedades involucradas en la igualdad y, posiblemente,
otras propiedades derivadas de las mismas.
En este caso no es necesario invocar el mtodo toString porque cuando un tipo objeto est dentro
de una operacin con otros operandos de tipo String el compilador llama automticamente al
mtodo. Es decir, en ese contexto el compilador convierte automticamente el tipo dado a String.

En relacin a la implementacin de hashCode :


o

El hashCode se calcula a partir de las propiedades involucradas en la igualdad. Si hay ms de una


podemos seguir la regla: El hashCode resultante es igual al hashCode de la primera propiedad ms
31 por el hashCode de la segunda propiedad ms 31*31 por el hashCode de la tercera propiedad,

11

12

Introduccin a la Programacin

etc. Alternativamente al 31 se podra haber escogido otro nmero primo no muy grande. El
escoger un nmero primo hace que el hashCode calculado se disperse adecuadamente. Es decir,
que los hashCode de dos objetos distintos sean en la mayora de los casos distintos.
Un ejemplo de invocacin a equals es el que se muestra a continuacin:
Ejemplo 2. Ejemplo de invocacin de equals

public class TestIgualdad {


public static void main(String[] args) {
Punto p1 = new PuntoImpl(6.0, 4.0);
Punto p2 = new PuntoImpl(6.0, 4.0);
if (p1.equals(p2) && p1 != p2) {
System.out.println(p1 + " y " + p2
+ " son iguales pero no idnticos");
}
}
}

6. El tipo Comparable
El tipo Comparable un orden a los objetos de un tipo creado por el usuario, al que llamaremos orden natural.
Java ya proporciona un orden natural a los tipos envoltorio como Integer y Double o al tipo inmutable String
(que extienden a Comparable) permitiendo que, por ejemplo, se puedan ordenar cuando estn sobre una Lista
o array.
El tipo Comparable est definido como2:
package java.lang;
public interface Comparable<T> {
int compareTo(T o);
}

El tipo Comparable est incluido en el paquete java.lang de Java y se compone de un slo mtodo: el mtodo
compareTo. El tipo Comparable usa tipos genricos y sirven para una relacin de orden total sobre los objetos
de un tipo dado. El tipo Comparable sirve para establecer el orden natural de un tipo dado, y su
implementacin tiene que cumplir una serie de restricciones:

El mtodo compareTo debe lanzar la excepcin NullPointerException cuando toma como parmetro un
valor null.
compareTo compara dos objetos this y p2 y devuelve un entero que es:
Negativo si this es menor que p2
Cero si this es igual a p2

Puede ver la documentacin del tipo Comparable en la API de Java:


https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html

2. Elementos del lenguaje Java

Positivo si this es mayor que p2


equals/compareTo: El orden natural definido debe ser coherente con la definicin de igualdad. Si equals
devuelve true, compareTo debe devolver cero. Aqu tambin incluimos, tal como se recomienda en la
documentacin de Java, la inversa. Es decir, que si compareTo devuelve cero, entonces equals devuelve
true. Esto lo podemos enunciar diciendo que la expresin siguiente es verdadera para cualquier par de
objetos x e y: (x.compareTo(y) == 0) (x.equals(y)).

En general, para implementar el mtodo compareTo usaremos los mtodos compareTo de las propiedades
involucradas en la igualdad o algunas otras derivadas. Un orden natural adecuado puede ser comparar en
primer lugar por una propiedad elegida arbitrariamente, si resulta cero comparar por la segunda propiedad,
etc. Veamos un ejemplo, definiendo el tipo Persona con el esquema de diseo de tipos usado en este tema.
Tipo Persona
Propiedades:
o DNI: String, consultable.
o Nombre: String, consultable y modificable.
o Apellidos: String, consultable y modificable.
Representacin como cadena: apellidos, nombre (dni)
Criterio de igualdad: Dos personas son iguales si sus dnis son iguales, sus apellidos son iguales y sus
nombres.
Criterio de ordenacin natural: Por apellidos, nombre y DNI.
Lo primero que debemos modificar respecto al esquema de diseo de tipos del tema 1 es su interfaz, ya que
al tener definido el tipo un orden natural tiene que extender a Comparable):
Ejemplo 3. Diseo de la interfaz Persona extendiendo a Comparable

public interface Persona extends Comparable<Persona> {


String getDNI();
String getNombre();
String getApellidos();
void setNombre(String nombre);
void setApellidos(String apellidos);
}

Los mtodos que habra que aadir a la clase PersonaImpl seran los relacionados con Object, y compareTo.
Ejemplo 4. Mtodos toString, equals, hashCode y compareTo en PersonaImpl

public String toString() {


String s = getApellidos() + ", " + getNombre() + "(" + getDNI() + ")";
return s;
}
public boolean equals(Object o) {
boolean r = false;
if (o instanceof Persona) {
Persona p = (Persona) o;
r = this.getDNI().equals(p.getDNI())
&& this.getNombre().equals(p.getNombre())
&& this.getApellidos().equals(p.getApellidos());

13

14

Introduccin a la Programacin

}
return r;
}
public int hashCode() {
return getDNI().hashCode() + getApellidos().hashCode() * 31
+ getNombre().hashCode() * 31 * 31;
}
public int compareTo(Persona p) {
int r;
if (p == null) {
throw new NullPointerException();
}
r = getApellidos().compareTo(p.getApellidos());
if (r == 0) {
r = getNombre().compareTo(p.getNombre());
if (r == 0) {
r = getDNI().compareTo(p.getDNI());
}
}
return r;
}

Note que el orden natural para el tipo Persona deber ser compatible con equals y, por tanto, deber tener
en cuenta las mismas propiedades escogidas para definir el criterio de igualdad.
Un ejemplo de uso del mtodo compareTo es el siguiente:
Ejemplo 5. Ejemplo de uso del mtodo compareTo

public TestCompareTo {
public static void main(String[] args) {
Persona p1 = new PersonaImpl("222222222-R", "John", "Doe");
Persona p2 = new PersonaImpl("111111111-A", "Jane", "Doe");
if (p1.compareTo(p2) > 0) {
System.out.println(p1 + " va despus de " + p2);
}
}
}

7. El tipo Comparator

2. Elementos del lenguaje Java

El Comparator tambin sirve para proporcionar un orden a los objetos de un tipo creado por el usuario, y sirve
para definir un criterio de ordenacin alternativo al proporcionado por el criterio de ordenacin natural. El
tipo Comparator est incluido en el paquete java.util y, aunque tiene un mtodo equals, solamente usaremos
el mtodo compare. Su definicin es la siguiente3:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
...
}

El tipo Comparator tambin usa tipos genricos y tambin define, al igual que Comparable, una relacin de
orden total. El mtodo compare compara los dos objetos que toma como parmetro y su implementacin
tiene que cumplir los siguientes requisitos:

compare compara dos objetos o1 y o,2 y devuelve un entero que es:


Negativo si o1 es menor que o2 , segn el criterio de ordenacin alternativo definido
Cero si o1 es igual a o2, segn el criterio de ordenacin alternativo definido
Positivo si o1 es mayor que o2, segn el criterio de ordenacin alternativo definido

equals/compare: Para el diseo de un Comparator, Java propone lo siguiente:


It is generally the case, but not strictly required that (compare(x,y)==0) == (x.equals(y)).
Generally speaking, any comparator that violates this condition should clearly indicate this fact. The
recommended language is "Note: this comparator imposes orderings that are inconsistent with equals."

Esta recomendacin tiene el problema de que si hacemos un Comparator coherente con equals y ste debe
ser coherente con compareTo.
Un criterio de ordenacin alternativo para Persona puede ser ordenar primero por DNI, luego por apellidos, y
luego por nombre. Un comparador para especificar este criterio de ordenacin alternativo es:
Ejemplo 6. Criterio de ordenacin alternativo para Persona.

import java.util.Comparator;
public class ComparadorPersonaDNI implements Comparator<Persona> {
public int compare(Persona p1, Persona p2) {
int r;
r = p1.getDNI().compareTo(p2.getDNI());
if (r == 0) {
r = p1.getApellidos().compareTo(p2.getApellidos());
if (r == 0) {
r = p1.getNombre().compareTo(p2.getNombre());
}
}
return r;
3

Puede obtener ms informacin del tipo Comparator en la documentacin de la API de Java:


https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

15

16

Introduccin a la Programacin

}
}

Como podemos comprobar la implementacin anterior consistente con la igualdad.


Un ejemplo de uso de Comparator es el siguiente:

Ejemplo 7. Ejemplo de uso de Comparator

public TestCompare {
public static void main(String[] args) {
Persona p1 = new PersonaImpl("222222222-R", "John", "Doe");
Persona p2 = new PersonaImpl("111111111-A", "Jane", "Doe");
Comparator<Persona> c = new ComparadorPersonaDNI();
if (compare(p1, p2) > 0) {
System.out.println(p1 + " va despus de " + p2);
}
}
}

Si quisiramos definir un criterio de ordenacin alternativo para ordenar las personas exclusivamente por su
nombre, tendramos un comparador que no es consistente con la igualdad. En este caso, si queremos
conseguir la consistencia con equals (es decir, que si el comparador devuelve cero, equals devuelve true)
hacemos una comparacin adicional, aunque la definamos explcitamente en el criterio de ordenacin
alternativo. Esta comparacin se har si los objetos son iguales segn el criterio de ordenacin alternativo. En
este caso se desempata con el orden natural del tipo. Con esto conseguimos, al menos que si compare da
cero entonces equals es true. As un comparador para ordenar las personas por su nombre sera:
Ejemplo 8. Comparador con desempate usando el orden natural.

import java.util.Comparator;
public class ComparadorPersonaNombre implements Comparator<Persona> {
public int compare(Persona p1, Persona p2) {
int r;
r = p1.getNombre().compareTo(p2.getNombre());
if (r == 0) {
r = p1.compareTo(p2);
}
return r;
}
}

Note que esta solucin del desempate con compareTo solamente es posible si el tipo tiene definido un orden
natural.

8. Ordenacin de arrays con la clase de utilidad Arrays.

2. Elementos del lenguaje Java

Uno de los usos habituales de los tipos Comparable y Comparator es ordenar los objetos que estn
organizados en colecciones o agregados. Como ya sabemos del tema anterior para modificar el orden de los
elementos en un array java proporciona la clase de utilidad Arrays4 con los siguientes mtodos estticos.

package java.util;
public class Arrays {
public static <T> List<T> asList(T...a);
//Existen versiones para double, float, char, byte, long y Object de:
// binarySearch, equals, fill, sort, toString
public static int binarySearch(int[] a, int key);
public static int binarySearch(int[] a, int fromIndex, int toIndex, int key);
public static boolean equals(int[] a, int[] a2);
public static void fill(int[] a, int val);
public static void fill(int[] a, int fromIndex, int toIndex, int val);
public static void sort(int[] a);
public static void sort(int[] a, int fromIndex, int toIndex);
public static void sort(Object[] a);
public static void sort(Object[] a, int fromIndex, int toIndex);
public static void sort(T[] a, Comparator<? super T> c);
public static void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T>
c);
public static String toString(int[] a);
...
}

Un ejemplo de uso de estos mtodos para ordenar un array es el siguiente:


public TestOrdenacion {
public static void main(String[] args) {
Persona p1 = new PersonaImpl("31122334-W", "Antonio",
"Garca Maratn");
Persona p2 = new PersonaImpl("34423123-V", "Mara", "Prez Pia");
Persona p3 = new PersonaImpl("38962334-W", "Eva", "Garca Morn);
Persona p4 = new PersonaImpl("34435723-V", "Eugenia", "Gmez Titos");
Persona[] v = {p1, p2, p3, p4};
System.out.println(Arrays.toString(v));
Arrays.sort(v);
System.out.println(
"El array tras ordenar segn criterio de orden natural: ");
System.out.println(Arrays.toString(v));
Arrays.sort(v, new ComparatorPersonaNombre());
System.out.println(
"El array tras ordenar segn criterio alternativo de nombre: ");
System.out.println(Arrays.toString(v));
}
}

Note que hay dos versiones del mtodo sort: una que no tiene un comparador como argumento, y que, por
lo tanto, ordena por el criterio de ordenacin natural, y otra, con un comparador, que ordena segn un criterio
de ordenacin alternativo.
4

Ver el resto de mtodos en: https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html

17

18

Introduccin a la Programacin

Ejercicios
1. Ample el tipo Libro definido en el Tema 1, para que tenga en cuenta las siguientes propiedades y criterios,
y sabiendo que ahora el tipo es Comparable.

Propiedades:
o ISBN, de tipo cadena, consultable. Debe tener obligatoriamente 0, 10 13 caracteres
o Ttulo, de tipo cadena, consultable y modificable.
o Autor, de tipo Persona, consultable y modificable.
o Nmero de pginas, de tipo entero, consultable y modificable. Debe ser mayor que cero.
o Precio, de tipo real, consultable y modificable. Debe ser mayor o igual que cero.
o Es best-seller, de tipo booleano, consultable y modificable.

Representacin como cadena: ISBN seguido de un guin y ttulo


Criterio de igualdad: Dos libros son iguales si tienen el mismo ttulo y ISBN
Orden natural: Por ttulo e ISBN
Orden alternativo: Precio y best-seller.

2. Tipo Rectangulo

Propiedades:
o Lado A: Double, consultable y modificable. Debe ser mayor o igual que cero.
o Lado B: Double, consultable y modificable. Debe ser mayor o igual que cero.
o Centro: Punto, consultable y modificable
o Angulo con Eje X: Double, consultable y modificable
o Area: Double, consultable, derivada, se calcula como el producto de los lados.
Representacin como cadena: lados, centro y ngulo separados por comas y entre llaves
Criterio de igualdad: Dos rectngulos son iguales si tienen los mismos lados, centro y ngulo
Criterio de ordenacin natural: No tiene.
Orden alternativo: rea.

Вам также может понравиться