1. Tema 3 – Herencia
Programación Orientada a Objetos
Curso 2010/2011
Primera parte. Herencia en Java
2. Programación Orientada a Objetos 2Curso 2010/2011
Contenido
• Introducción.
• Definición y tipos.
• Constructores.
• Redefinición.
• Restringir la herencia.
• Visibilidad protegida.
• Polimorfismo.
• Herencia y sistema de
tipos.
• Ligadura dinámica.
• Clase Object.
• Autoboxing.
• Copia de objetos.
• Igualdad de objetos.
• Casting
• Operador instanceof
• Clases abstractas.
• Interfaces.
3. Programación Orientada a Objetos 3Curso 2010/2011
Introducción
Entre las clases pueden existir relaciones conceptuales:
Extensión, Especialización, Combinación.
Ejemplos:
“Una pila puede definirse a partir de una cola o viceversa”
“Un rectángulo es una especialización de polígono”
“Libros y Revistas tienen propiedades comunes”
Herencia:
Mecanismo para definir y utilizar estas relaciones.
Permite la definición de una clase a partir de otra.
4. Programación Orientada a Objetos 4Curso 2010/2011
Introducción
La herencia organiza las clases en una estructura
jerárquica Jerarquía de clases
No es sólo un mecanismo de reutilización de código.
Es consistente con el sistema de tipos.
Ejemplos:
PUBLICACION
LIBRO REVISTA
LIBRO_TEXTO INVESTIGACION MAGAZINE
FIGURA
POLIGONO CIRCULO
RECTANGULO
5. Programación Orientada a Objetos 5Curso 2010/2011
Herencia
Si la clase B hereda de A, entonces B incorpora la
estructura (atributos) y comportamiento (métodos) de la
clase A, pero puede incluir adaptaciones:
B puede añadir nuevos atributos.
B puede añadir nuevos métodos.
B puede redefinir métodos heredados (refinar o
reemplazar).
En general, las adaptaciones dependen del lenguaje OO.
6. Programación Orientada a Objetos 6Curso 2010/2011
Herencia – Terminología
A
B
C
B hereda de A (A es la superclase y B la subclase)
A es la clase padre o clase base de B
C hereda de B y A
B y C son subclases de A
B es un descendiente directo de A
C es un descendiente indirecto de A
A y B son ascendientes de C
7. Programación Orientada a Objetos 7Curso 2010/2011
Tipos de herencia
Herencia simple:
Una clase puede heredar
de una única clase.
Java, C#
Herencia múltiple:
Una clase puede heredar
de varias clases.
C++
A
ED
CB
A
B C
8. Programación Orientada a Objetos 8Curso 2010/2011
Reconocer la herencia
Especialización:
Se detecta que una clase es un caso especial de otra.
Ejemplo: Rectángulo es un tipo de Polígono.
Generalización (factorización):
Se detectan dos clases con características en común y se
crea una clase padre con esas características.
Ejemplo: Libro, Revista Publicación
No hay receta mágica para crear buenas jerarquías de
herencia.
9. Programación Orientada a Objetos 9Curso 2010/2011
Caso de estudio
Continuación de la aplicación de gestión
bancaria.
Se introduce el concepto Depósito que permite a
un cliente obtener intereses por su dinero.
Un Depósito, simplificado, se define:
Propiedades: titular, capital, plazo en días y tipo de
interés.
Comportamiento:
Permite liquidar el depósito devolviendo el capital
depositado más los intereses.
Permite consultar los intereses generados al
vencimiento.
10. Programación Orientada a Objetos 10Curso 2010/2011
Clase Deposito 1/2
public class Deposito {
private Persona titular;
private double capital;
private int plazoDias;
private double tipoInteres;
public Deposito(Persona titular, double capital,
int plazoDias, double tipoInteres) {
this.titular = titular;
this.capital = capital;
this.plazoDias = plazoDias;
this.tipoInteres = tipoInteres;
}…
11. Programación Orientada a Objetos 11Curso 2010/2011
Clase Deposito 2/2
public class Deposito {
…
public double getCapital() { … }
public int getPlazoDias() { … }
public double getTipoInteres() { … }
public Persona getTitular() { … }
public double liquidar() {
return getCapital() + getIntereses();
}
public double getIntereses() {
return (plazoDias * tipoInteres * capital)/365;
}
}
12. Programación Orientada a Objetos 12Curso 2010/2011
Caso de Estudio
Identificamos el concepto Depósito Estructurado que se
caracteriza por tener una parte del capital a tipo fijo y otra
parte a tipo variable.
¿Depósito Estructurado es un Depósito?
Comparte todas las características de Depósito.
Añade nuevas características.
La clase DepositoEstructurado hereda de
Deposito:
Nuevas características: capital a tipo variable y el tipo
de interés variable.
13. Programación Orientada a Objetos 13Curso 2010/2011
Clase DepositoEstructurado
public class DepositoEstructurado extends Deposito {
private double tipoInteresVariable;
private double capitalVariable;
public DepositoEstructurado(…) {…}
public double getInteresesVariable() {
return (getPlazoDias() * tipoInteresVariable
* capitalVariable)/365;
}
public void setTipoInteresVariable(…
public double getTipoInteresVariable() { … }
public double getCapitalVariable() { … }
}
14. Programación Orientada a Objetos 14Curso 2010/2011
Clase DepositoEstructurado
La subclase incluye las características específicas:
Atributos: tipo de interés variable y capital a tipo variable.
Método de consulta (get) del capital variable.
Métodos de consulta/establecimiento (get/set) del tipo de
interés variable.
Método para el cálculo de los intereses del capital a tipo
variable.
La clase hija hereda todos los atributos de la clase
padre, aunque no los vea debido a la ocultación de la
información.
La clase dispone de los métodos heredados como si fueran
propios (ejemplo, getPlazoDias()).
16. Programación Orientada a Objetos 16Curso 2010/2011
Herencia y constructores
En Java los constructores no se heredan.
El constructor de un DepositoEstructurado se
declara con los mismos parámetros que un Deposito y
añade la cantidad de capital a tipo variable.
La clase DepositoEstructurado no tiene visibilidad
sobre los atributos privados de Deposito.
Java permite invocar a los constructores de la clase
padre dentro de un constructor utilizando la llamada
super(…)
17. Programación Orientada a Objetos 17Curso 2010/2011
Clase DepositoEstructurado
public class DepositoEstructurado extends Deposito {
…
public DepositoEstructurado(Persona titular,
double capitalFijo, double capitalVariable,
int plazoDias, double tipoInteresFijo) {
super(titular, capitalFijo, plazoDias,
tipoInteresFijo);
this.capitalVariable = capitalVariable;
}
…
}
18. Programación Orientada a Objetos 18Curso 2010/2011
Herencia y constructores
Cuando se aplica herencia, la llamada a un constructor
de la clase padre es obligatoria.
Debe ser la primera sentencia del código del constructor.
Si se omite la llamada, el compilador asume que la
primera llamada es super()
Llama al constructor sin argumentos de la clase padre.
Si no existe ese constructor, la clase no compila.
19. Programación Orientada a Objetos 19Curso 2010/2011
Adaptación del código heredado
La clase DepositoEstructurado añade nuevas
propiedades: capital a tipo variable y tipo de interés
variable.
Sin embargo, hay que adaptar dos métodos heredados:
getCapital(): el capital de un depósito estructurado es el
capital a tipo fijo más el capital a tipo variable.
getIntereses(): intereses a tipo fijo más intereses a tipo
variable.
La herencia permite la redefinición de métodos para
adaptarlos a la semántica de la clase.
20. Programación Orientada a Objetos 20Curso 2010/2011
Redefinición de métodos
La redefinición de un método heredado puede ser de dos
tipos:
Refinamiento: se añade nueva funcionalidad al
comportamiento heredado.
Reemplazo: se sustituye completamente la implementación
del método heredado.
En el refinamiento de un método resulta útil invocar a la
versión heredada del método.
21. Programación Orientada a Objetos 21Curso 2010/2011
Redefinición de métodos y super
El lenguaje proporciona la palabra reservada super que
permite llamar a la versión del padre de un método que
se redefine en la clase.
En general, el uso de super se recomienda sólo para el
refinamiento de métodos.
No hay que utilizar super para llamar a métodos
que se heredan.
Sin embargo, hay excepciones
getCapital(): retorna el capital total del depósito.
La versión del padre (super.getCapital()) devuelve la
parte del capital a tipo fijo. Puede resultar útil en la
implementación de la clase.
22. Programación Orientada a Objetos 22Curso 2010/2011
Clase DepositoEstructurado
public class DepositoEstructurado extends Deposito {
...
@Override
public double getIntereses() {
return super.getIntereses() + getInteresesVariable();
}
@Override
public double getCapital() {
return super.getCapital() + getCapitalVariable();
}
…
}
23. Programación Orientada a Objetos 23Curso 2010/2011
Caso de estudio
Identificamos un nuevo tipo de depósito formado por una
parte del capital a tipo fijo y otra a tipo variable con un
interés mínimo garantizado (Depósito Garantizado).
Este depósito posee todas las características de un
Depósito Estructurado y añade una garantía al interés
variable (regla es un).
La clase DepositoGarantizado hereda de
DepositoEstructurado.
24. Programación Orientada a Objetos 24Curso 2010/2011
Clase DepositoGarantizado
public class DepositoGarantizado
extends DepositoEstructurado {
private double tipoInteresGarantizado;
public DepositoGarantizado(…,
{
super(titular, capitalFijo, capitalVariable,
plazoDias, tipoInteresFijo);
this.tipoInteresGarantizado = tipoIntersesGarantizado;
setTipoInteresVariable(tipoIntersesGarantizado);
}
}
25. Programación Orientada a Objetos 25Curso 2010/2011
Redefinición de métodos
Esta clase debe controlar que al establecer el tipo de interés
variable no quede por debajo del interés garantizado.
Para ello, redefine (refina) el método
setTipoInteresVariable().
@Override
public void setTipoInteresVariable(
double tipoInteresVariable) {
if (tipoInteresVariable >= tipoInteresGarantizado)
super.setTipoInteresVariable(tipoInteresVariable);
}
}
26. Programación Orientada a Objetos 26Curso 2010/2011
Caso de estudio
Un Depósito Penalizable es un depósito básico en el que
los intereses pueden ser penalizados.
El nuevo depósito tiene todas las características de un
depósito básico (regla es un).
Clase DepositoPenalizable hereda de Deposito
La penalización consiste en reducir el interés del depósito
a la mitad.
Adapta el cálculo de los intereses según la penalización.
27. Programación Orientada a Objetos 27Curso 2010/2011
Clase DepositoPenalizable
public class DepositoPenalizable extends Deposito {
private boolean penalizado;
public DepositoPenalizable(…) {
super(titular, capital, plazoDias, tipoInteres);
penalizado = false;
}
@Override
public double getIntereses() {
if (penalizado)
return super.getIntereses() / 2;
else return super.getIntereses();
}
public boolean isPenalizado() {…}
public void setPenalizado(boolean penalizado) {…}
}
29. Programación Orientada a Objetos 29Curso 2010/2011
Redefinición
En los ejemplos se comprueba que la redefinición
reconcilia la reutilización con la extensibilidad:
Es habitual hacer cambios cuando se reutiliza un código.
Los atributos no se pueden redefinir, sólo se ocultan:
Si la clase hija define un atributo con el mismo nombre que
un atributo de la clase padre, éste no es accesible.
El atributo del padre existe, pero no se puede ver.
Un método es una redefinición si tiene la misma
signatura (nombre y parámetros) que un método de la
clase padre:
Si se cambia el tipo de los parámetros se está
sobrecargando el método heredado.
30. Programación Orientada a Objetos 30Curso 2010/2011
Restringir la herencia
¿Es seguro permitir que los subtipos redefinan cualquier
método?
La redefinición incorrecta del algoritmo del método
liquidar() podría comprometer la consistencia y
seguridad de la aplicación.
En Java se puede aplicar el modificador final a un
método para indicar que no puede ser redefinido.
Asimismo, el modificador final es aplicable a una clase
indicando que no se puede heredar de ella.
31. Programación Orientada a Objetos 31Curso 2010/2011
Restringir la herencia
public class Deposito {
…
public final double liquidar() {
return getCapital() + getIntereses();
}
}
El algoritmo del método liquidar es el mismo para todos
los depósitos, no se puede redefinir.
32. Programación Orientada a Objetos 32Curso 2010/2011
Visibilidad para la herencia
Puede tener sentido que algunos atributos y métodos de
una clase, sin ser públicos, puedan ser accesibles a las
subclases.
Ejemplo: el atributo capital de Deposito.
Nivel de visibilidad protegido (protected): visibilidad para
las subclases y las clases del mismo paquete.
Es discutible el uso de visibilidad protegida para los
atributos
En contra de la ocultación de la información.
Para los métodos, la visibilidad protegida es útil.
33. Programación Orientada a Objetos 33Curso 2010/2011
Niveles de visibilidad
En Java, los niveles de visibilidad son incrementales
private
() paquete
protected
public public todo el código
protected clase + paquete + subclases
(nada) clase + paquete
private clase
34. Programación Orientada a Objetos 34Curso 2010/2011
Revisión redefinición de métodos
Al redefinir un método podemos cambiar su
implementación (refinamiento, reemplazo).
También se puede cambiar el nivel de visibilidad de la
declaración para incrementarlo.
Es posible cambiar el tipo de retorno a otro más
específico (subclase) Regla covariante.
Ejemplo: en DepositoGarantizado podemos obligar a
que los titulares sean de tipo PersonaPreferente,
subclase de Persona.
En este caso, el método de consulta se redefine como
PersonaPreferente getTitular().
35. Programación Orientada a Objetos 35Curso 2010/2011
Polimorfismo
Capacidad de una entidad (atributo, variable, parámetro)
de referenciar en tiempo de ejecución a objetos de
diferentes clases.
Es restringido por herencia.
Fundamental para escribir código reutilizable.
El polimorfismo implica que una entidad tiene un tipo
estático (declarado) y otro dinámico (al que referencia la
entidad).
36. Programación Orientada a Objetos 36Curso 2010/2011
Tipos estático y dinámico
Tipo estático (te):
Tipo asociado a la declaración de una entidad.
Tipo dinámico:
Tipo correspondiente a la clase del objeto conectado a la
entidad en tiempo de ejecución.
Conjunto de tipos dinámicos (ctd):
Conjunto de posibles tipos dinámicos de una entidad.
Ejemplo:
A
ED
CB
F
A oa; B ob; C oc;
te(oa) = A ctd(oa) = {A,B,C,D,E,F}
te(ob) = B ctd(ob) = {B, D, E}
te(oc) = C ctd(oc) = {C,F}
37. Programación Orientada a Objetos 37Curso 2010/2011
Polimorfismo
Asignación polimórfica:
La variable deposito tiene como tipo estático Deposito.
El tipo dinámico de la variable cambia:
Línea 1: clase Deposito.
Línea 3: clase DepositoPenalizable.
1. Deposito deposito = new Deposito(…);
2. DepositoPenalizable penalizable =
new DepositoPenalizable(…);
// Asignación polimórfica
3. deposito = penalizable;
38. Programación Orientada a Objetos 38Curso 2010/2011
Herencia y sistema de tipos
¿Serían posibles las siguientes asignaciones?
Objeto DepositoEstructurado a variable Deposito.
Objeto DepositoEstructurado a variable
DepositoPenalizable.
Objeto DepositoGarantizado a variable Deposito.
¿Serían válidos los siguientes mensajes?
Mensaje getCapitalVariable() sobre una variable de
tipo estático Deposito.
Mensaje getCapital() sobre una variable de tipo estático
DepositoGarantizado.
39. Programación Orientada a Objetos 39Curso 2010/2011
Compatibilidad de tipos
Una clase (tipo) B es compatible con otra clase A si B es
descendiente de A:
DepositoEstructurado es compatible con Deposito.
DepositoGarantizado es compatible con Deposito y
DepositoEstructrado.
DepositoEstructurado NO es compatible con
DepositoPenalizable.
40. Programación Orientada a Objetos 40Curso 2010/2011
Compatibilidad de tipos
Una asignación polimórfica es válida si el tipo estático
de la parte derecha es compatible con el tipo estático de la
parte izquierda:
Deposito deposito = new DepositoEstructurado(…);
DepositoPenalizable penalizable =
new DepositoPenalizable(…);
// Asignación polimórfica válida
deposito = penalizable;
// Asignación polimórfica no válida
DepositoGarantizado garantizado = penalizable;
41. Programación Orientada a Objetos 41Curso 2010/2011
Compatibilidad de tipos
Método polimórfico: un paso de parámetros es válido si
el tipo estático del parámetro real es compatible con el tipo
estático del parámetro formal.
public double indiceRentabilidad(Deposito deposito) {
return deposito.getIntereses()/deposito.getCapital();
}
DepositoPenalizable penalizable =
new DepositoPenalizable( … );
banco.indiceRentabilidad(penalizable);
42. Programación Orientada a Objetos 42Curso 2010/2011
Validez de mensajes
La herencia es consistente con el sistema de tipos.
Sobre una variable cuyo tipo estático es
DepositoEstructurado podemos aplicar:
Métodos heredados y redefinidos: getIntereses(),
getPlazoDias(), etc.
Métodos propios: getCapitalVariable(), etc.
Si el tipo estático es Deposito no podemos aplicar métodos
de los subtipos:
deposito.getTipoInteresVariable(); //Error
deposito.isPenalizado(); // Error
43. Programación Orientada a Objetos 43Curso 2010/2011
Validez de mensajes
Deposito deposito = new Deposito(…);
DepositoEstructurado estructurado = new DepositoEstructurado(…);
estructurado.liquidar(); //OK HEREDADO DE DEPOSITO
estructurado.getCapital(); //OK METODO REDEFINIDO
estructurado.getCapitalVariable(); //OK MÉTODO PROPIO
deposito = estructurado;
deposito.getIntereses(); //OK METODO DE DEPOSITO
deposito.getCapitalVariable(); //ERROR COMPILACION!
44. Programación Orientada a Objetos 44Curso 2010/2011
Política pesimista de tipos
El compilador rechazaría los siguientes casos:
Deposito deposito;
DepositoPenalizable penalizable = new …;
deposito = penalizable;
penalizable = deposito; // Error de compilación
Deposito deposito;
DepositoPenalizable penalizable = new …;
deposito = penalizable;
deposito.isPenalizado(); // Error de compilación
45. Programación Orientada a Objetos 45Curso 2010/2011
Ligadura dinámica
¿Qué versión del método getCapital() se ejecutaría?
Deposito deposito;
if (clientePreferente)
deposito = new DepositoGarantizado(...);
else
deposito = new DepositoPenalizable(...);
deposito.getCapital();
46. Programación Orientada a Objetos 46Curso 2010/2011
Ligadura dinámica
La versión de un método en una clase es la introducida por la
clase (redefinida) o la heredada.
Versiones del método getCapital():
Versión Deposito: el método es definido en Depósito
La clase DepositoPenalizable lo hereda.
Versión DepositoEstructurado: la clase
DepositoEstructurado redefine el método.
La clase DepositoGarantizado hereda la versión de
DepositoEstructurado.
Se ejecuta la versión asociada al tipo dinámico del objeto.
47. Programación Orientada a Objetos 47Curso 2010/2011
Ligadura dinámica
public double posicionGlobal(Deposito[] depositos) {
double posicion = 0;
for (Deposito deposito : depositos) {
posicion += deposito.getCapital();
}
return posicion;
}
¿Qué sucedería si apareciera un nuevo tipo de depósito?
48. Programación Orientada a Objetos 48Curso 2010/2011
Ligadura dinámica
El método getCapital() aplicado sobre una referencia
de tipo estático Deposito puede adoptar dos versiones.
La misma llamada a un método puede tener distintas
interpretaciones.
Para que funcione la ligadura dinámica es necesario que
la variable sobre la que se aplica el método sea
polimórfica (en Java siempre es así).
49. Programación Orientada a Objetos 49Curso 2010/2011
¿Qué versión se
ejecuta?
A oa;
B ob = new B();
D od = new D();
oa = ob;
oa.f();
oa = od;
oa.f();
C
A
B
{fαααα}
redefine f
redefine f
D {fδδδδ}
{fββββ}
Ligadura dinámica
Sea el mensaje x.f (), la comprobación estática de tipos
garantiza que al menos existirá una versión aplicable para f, y la
ligadura dinámica garantiza que se ejecutará la versión más
apropiada.
50. Programación Orientada a Objetos 50Curso 2010/2011
Método polimórfico
Método que puede recibir distintos tipos de argumentos
manteniendo el mismo comportamiento.
La ejecución correcta se determina en tiempo de ejecución
aplicando ligadura dinámica.
public double indiceRentabilidad(Deposito deposito) {
return deposito.getIntereses()/deposito.getCapital();
}
51. Programación Orientada a Objetos 51Curso 2010/2011
Código reutilizable
Un único código con diferentes interpretaciones en tiempo
de ejecución.
Ejercicio:
¿Qué métodos y versiones se ejecutan cuando se aplica el
método liquidar() a un depósito penalizable?
¿Y para un depósito estructurado?
Deposito deposito =
new DepositoPenalizable(...);
deposito.liquidar();
52. Programación Orientada a Objetos 52Curso 2010/2011
Código reutilizable
El método liquidar() es un ejemplo de código
reutilizable:
El mismo código es reutilizado por los subtipos.
En tiempo de ejecución, el código ejecutado varía según el
tipo de depósito al que sea aplicado.
El polimorfismo, la redefinición de métodos y la ligadura
dinámica permiten reutilizar el código y al mismo tiempo
adaptarlo a las características de cada depósito.
53. Programación Orientada a Objetos 53Curso 2010/2011
Clase Object
El lenguaje Java define la clase Object como raíz de la
jerarquía de todas las clases del lenguaje.
Todas las clases heredan directa o indirectamente de la
clase Object:
Si una clase no hereda de ninguna otra, el compilador añade
extends Object a su declaración.
Una variable de tipo Object puede apuntar a cualquier
tipo del lenguaje:
Objetos creados a partir de clases.
Tipos primitivos gracias al autoboxing.
54. Programación Orientada a Objetos 54Curso 2010/2011
Clase Object y Autoboxing
El autoboxing es un mecanismo automático encargado de
convertir objetos en tipos primitivos y viceversa.
De este modo, una referencia de tipo Object puede
apuntar a cualquier tipo de datos.
Conversión de tipos primitivos a objetos:
Java construye objetos envoltorio donde almacenar los
valores primitivos.
Para cada tipo primitivo hay una clase envoltorio: Integer
para int, Double para double, etc.
La conversión de un objeto en un tipo primitivo requiere
hacer el casting al tipo envoltorio.
Object obj = 3;
int entero = (Integer)obj;
55. Programación Orientada a Objetos 55Curso 2010/2011
Métodos clase Object
En Java, la clase Object incluye las características
comunes a todos los objetos:
public boolean equals(Object obj)
Igualdad de objetos
protected Object clone()
Clonación de objetos
public String toString()
Representación textual de un objeto
public final Class getClass()
Clase a partir de la que ha sido instanciado un objeto.
public int hashCode()
Código hash utilizado en las colecciones.
56. Programación Orientada a Objetos 56Curso 2010/2011
Copia de objetos
La asignación de referencias (=) copia el oid del objeto y
no la estructura de datos.
Para obtener una copia de un objeto hay que aplicar el
método clone.
El método clone está implementado en la clase Object
(es heredado) pero no es aplicable por otras clases
(visibilidad protected).
La clase debe redefinir el método clone para aumentar
la visibilidad y crear una copia que se adapte a sus
necesidades.
La versión de la clase Object (super.clone())
construye una copia superficial de la instancia actual.
57. Programación Orientada a Objetos 57Curso 2010/2011
Tipos de copia
Tipos de copia:
Copia superficial: los campos de la copia son
exactamente iguales a los del objeto receptor.
Copia profunda: los campos primitivos de la copia son
iguales y las referencias a objetos son copias
profundas.
Adaptada: adaptada a la necesidad de la aplicación.
58. Programación Orientada a Objetos 58Curso 2010/2011
Copia superficial de Cuenta
Copia
superficial:
Aliasing
incorrecto al
compartir las
últimas
operaciones.
No deberían
tener el mismo
código
J. Gomez
87654321
nombre
dni
Objeto Persona
-5000
10000
123456
100000
titular
codigo
saldo
ultOper
cuenta
123456
100000
titular
codigo
saldo
ultOper
copia
59. Programación Orientada a Objetos 59Curso 2010/2011
Copia profunda de Cuenta
Copia profunda:
No tiene sentido duplicar el objeto persona y que
tengan el mismo código.
J. Gomez
87654321
-5000
10000
123456
100000
titular
codigo
saldo
ultOper
cuenta
123456
100000
titular
codigo
saldo
ultOper
copia
-5000
10000
J. Gomez
87654321
60. Programación Orientada a Objetos 60Curso 2010/2011
Copia correcta de Cuenta
Copia adaptada: cumple los requisitos de la
aplicación
J. Gomez
87654321
-5000
10000
123456
100000
titular
codigo
saldo
ultOper
cuenta
326457
100000
titular
codigo
saldo
ultOper
copia
-5000
10000
61. Programación Orientada a Objetos 61Curso 2010/2011
Método clone en Cuenta
public class Cuenta implements Cloneable{
…
public Cuenta clone(){
Cuenta copia = null;
try{
copia = (Cuenta)super.clone();
copia.codigo = ++ultimoCodigo;
copia.ultimasOperaciones =
(double[])ultimasOperaciones.clone();
}catch (CloneNotSupportedException e){
// No sucede si la clase es Cloneable
}
return copia;
}
}
62. Programación Orientada a Objetos 62Curso 2010/2011
Igualdad de objetos
El operador == se utiliza para consultar la identidad de
referencias dos referencias son idénticas si contienen
el mismo oid.
Identidad entre referencias:
a == c; // True
a == b; // False
El método equals permite implementar la igualdad de
objetos.
b
a
c
d
“uno”
23
“uno”
23
“dos”
23
63. Programación Orientada a Objetos 63Curso 2010/2011
Método equals
La implementación en la clase Object consiste en
comprobar la identidad del objeto receptor y el
parámetro.
Por tanto: a.equals(b); // False
Es necesario redefinir el método equals en las clases
donde necesitemos la operación de igualdad.
Sin embargo, hay que elegir la semántica de igualdad
más adecuada para la clase.
public boolean equals(Object obj) {
return this == obj;
}
64. Programación Orientada a Objetos 64Curso 2010/2011
Tipos de igualdad
Tipos de igualdad:
Superficial: los campos primitivos de los dos objetos son
iguales y las referencias a objetos idénticas (comparten los
mismos objetos).
Profunda: los campos primitivos son iguales y las
referencias son iguales (equals) en profundidad.
Adaptada: adaptada a las necesidades de la aplicación.
¿Cuándo dos cuentas son iguales?
65. Programación Orientada a Objetos 65Curso 2010/2011
Método equals en Cuenta
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (obj.getClass() != this.getClass())
return false;
Cuenta otraCta = (Cuenta)obj;
return (this.codigo == otraCta.codigo);
}
66. Programación Orientada a Objetos 66Curso 2010/2011
Método toString en Cuenta
@Override
public String toString() {
return getClass().getName() +
"[codigo = " + codigo +
", titular = "+ titular +
", saldo = "+saldo+
",ultimasOperaciones = " + ultimasOperaciones +
"]";
}
Patrón de redefinición del método toString
67. Programación Orientada a Objetos 67Curso 2010/2011
Métodos clase Object en jerarquía
En la redefinición de los métodos de la clase
Object hay que tener en cuenta si la clase es la
raíz de una jerarquía o un subtipo.
En el seminario 2 se desarrolla un ejemplo de
jerarquía de herencia en el que se implementan
los métodos de la clase Object.
68. Programación Orientada a Objetos 68Curso 2010/2011
Raíz de una jerarquía
Resulta muy útil disponer de una clase (tipo) que
represente la raíz de una jerarquía de clases.
Permite utilizar estructuras de datos polimórficas:
La clase raíz también permite incluir las características
comunes a toda la jerarquía.
Deposito[] depositos = new Deposito[3];
depositos[0] = new DepositoPenalizable (…);
depositos[1] = new DepositoEstructurado (…);
depositos[2] = new DepositoGarantizado (…);
69. Programación Orientada a Objetos 69Curso 2010/2011
Caso de estudio
Motivación: ¿Cómo podemos establecer el tipo de interés
variable sobre todos los depósitos estructurados?
En la aplicación almacenamos todos los depósitos en un
array:
Deposito[] depositos;
Una posición del array puede apuntar a cualquier tipo de
depósito (estructura de datos polimórfica).
Sobre una referencia de tipo estático Deposito sólo
puedo aplicar métodos de esa clase.
Solución: casting (narrowing).
70. Programación Orientada a Objetos 70Curso 2010/2011
Casting
El compilador permite hacer un casting de una variable
polimórfica a uno de los posibles tipos dinámicos de la
variable.
Sería posible hacer un casting al tipo
DepositoEstructurado que tiene el método para
establecer el tipo de interés variable:
public void setTipoInteresVariable(double tipo,
Deposito[] depositos) {
for (Deposito d : depositos) {
DepositoEstructurado de = (DepositoEstructurado) d;
de.setTipoInteresVariable(tipo);
}
}
71. Programación Orientada a Objetos 71Curso 2010/2011
Operador instanceof
Problema:
Todos los depósitos no son estructurados.
El casting se resuelve en tiempo de ejecución y si es
incorrecto aborta el programa.
Solución: operador instanceof
Permite consultar en tiempo de ejecución si el tipo dinámico
de una variable es compatible con un tipo.
Se entiende por tipo compatible el tipo de la consulta o
cualquiera de los subtipos.
No es lo mismo preguntar por compatibilidad de tipos que
por la clase del objeto: getClass().
72. Programación Orientada a Objetos 72Curso 2010/2011
Operador instanceof
public void setTipoInteresVariable(double tipo,
Deposito[] depositos) {
for (Deposito d : depositos) {
if (d instanceof DepositoEstructurado) {
DepositoEstructurado de = (DepositoEstructurado) d;
de.setTipoInteresVariable(tipo);
}
}
}
73. Programación Orientada a Objetos 73Curso 2010/2011
Operador instanceof vs getClass()
Al utilizar getClass()se pregunta por el tipo exacto del
depósito.
Sería incorrecto, ya que los Depósitos Garantizados
(subtipo) quedarían fuera.
public void setTipoInteresVariable(double tipo,
Deposito[] depositos) {
for (Deposito d : depositos) {
if (d.getClass() == DepositoEstructurado.class) {
…
}
74. Programación Orientada a Objetos 74Curso 2010/2011
Clases abstractas
Motivación:
En la aplicación bancaria observamos que tanto las cuentas
como los depósitos tienen dos características en común:
tienen un titular y pagan impuestos.
Identificamos el concepto Producto Financiero y
definimos una clase que sea padre de Deposito y
Cuenta (Generalización).
Se realiza la refactorización del código de las clases
Deposito y Cuenta para subir la funcionalidad relativa al
titular a la nueva clase.
Además se introduce el método getImpuestos() que
calcula los impuestos como el 18% del beneficio del
producto.
76. Programación Orientada a Objetos 76Curso 2010/2011
Clase ProductoFinanciero
public class ProductoFinanciero {
private Persona titular;
public ProductoFinanciero(Persona titular) {
this.titular = titular;
}
public Persona getTitular() { return titular; }
public double getImpuestos() {
return getBeneficio() * 0.18;
}
}
77. Programación Orientada a Objetos 77Curso 2010/2011
Clases abstractas
¿Cómo se calcula el beneficio de un producto financiero?
¿Tiene sentido incluir una implementación por defecto que
retorne cero?
El método getBeneficio() no puede ser implementado
en la clase ProductoFinanciero.
El método getBeneficio() es abstracto.
Es responsabilidad de las subclases implementarlo
adecuadamente.
La clase ProductoFinanciero es abstracta, ya que tiene
un método abstracto.
78. Programación Orientada a Objetos 78Curso 2010/2011
Clase ProductoFinanciero
public abstract class ProductoFinanciero {
private Persona titular;
public ProductoFinanciero(Persona titular) {
this.titular = titular;
}
public Persona getTitular() { return titular; }
public double getImpuestos() {
return getBeneficio() * 0.18;
}
public abstract double getBeneficio();
}
80. Programación Orientada a Objetos 80Curso 2010/2011
Clases abstractas
Una clase abstracta define un tipo, como cualquier otra
clase.
Sin embargo, no se pueden construir objetos de una
clase abstracta.
Los constructores sólo tienen sentido para ser utilizados
en las subclases.
Justificación de una clase abstracta:
declara o hereda métodos abstractos
y/o representa un concepto abstracto para el que no tiene
sentido crear objetos: ¿un objeto producto financiero o figura
geométrica?
81. Programación Orientada a Objetos 81Curso 2010/2011
Clases parcialmente abstractas
Clases que tienen métodos abstractos y efectivos
(implementados)
Ejemplo: Producto Financiero.
Método plantilla: método efectivo que hace uso de
métodos abstractos.
Ejemplo: getImpuestos().
Importante mecanismo para definir código reutilizable.
Definen comportamiento abstracto común a todos los
descendientes.
82. Programación Orientada a Objetos 82Curso 2010/2011
Limitaciones de la herencia
La compatibilidad de tipos que proporciona la herencia
resulta útil: código reutilizable, variables y estructuras
polimórficas, etc.
Los tipos que representa una clase son:
El tipo que define la clase.
El tipo de cada uno de sus ascendientes.
La herencia simple limita el número de tipos que pueden
representar los objetos de una clase.
Solución: interfaces
83. Programación Orientada a Objetos 83Curso 2010/2011
Interfaces
Construcción proporcionada por Java para la
definición de tipos (sin implementación).
Se dice que una clase implementa una interfaz.
El número de interfaces que puede implementar
una clase no está limitado (implementación
múltiple de interfaces).
Los tipos que representan los objetos de una
clase pueden ser ampliados con la
implementación de interfaces.
84. Programación Orientada a Objetos 84Curso 2010/2011
Interfaz Amortizable
Ejemplo: un deposito es amortizable si permite rescatar
parte del capital antes del vencimiento.
Se define este tipo de depósitos como una interfaz.
public interface Amortizable {
boolean amortizar(double cantidad);
}
85. Programación Orientada a Objetos 85Curso 2010/2011
Interfaz Amortizable
Por defecto, en una interfaz la visibilidad de las
declaraciones es pública.
Las interfaces pueden incorporar declaraciones
(simplificadas) de constantes:
public interface Amortizable {
double LIMITE = 10000.0;
boolean amortizar(double cantidad);
}
86. Programación Orientada a Objetos 86Curso 2010/2011
Implementación de una interfaz
Ejemplo: sólo los depósitos garantizados y los
penalizables pueden ser amortizables.
Las dos clases implementan la interfaz:
public class DepositoPenalizable extends Deposito
implements Amortizable {
…
@Override
public boolean amortizar(double cantidad) {
if (cantidad > capital)
return false;
capital = capital - cantidad;
return true;
}
}
88. Programación Orientada a Objetos 88Curso 2010/2011
Extensión de interfaces
Una interfaz puede extender otras interfaces:
public interface Flexible extends Amortizable, Incrementable {
void actualizarTipoInteres(double tipo);
}
89. Programación Orientada a Objetos 89Curso 2010/2011
Interfaces – Resumen
Las interfaces definen tipos que deben ser
implementados por clases.
Amortizable deposito = new DepositoPenalizable(…);
Si una clase no implementa algún método de una interfaz
debe declararse abstracta.
Una interfaz define un tipo, pero no se pueden construir
objetos de una interfaz:
Amortizable deposito = new Amortizable(); // Error
Las interfaces resuelven la limitación de tipos impuesta
por la herencia simple.
90. Programación Orientada a Objetos 90Curso 2010/2011
Consejos uso de la herencia
Los atributos y métodos comunes deben situarse en clases
altas de la jerarquía (generalización).
No abusar de atributos protegidos. Compromete el
principio de ocultación de la información.
Aplica herencia si entre dos clases existe la relación es-un.
No debe utilizarse herencia salvo que todos los métodos
heredados tengan sentido.
En la redefinición de un método no hay que cambiar la
semántica que tiene definida.
Aplica polimorfismo y ligadura dinámica para evitar
análisis de casos.
91. Programación Orientada a Objetos 91Curso 2010/2011
Seminarios 2 y 3
Este tema se completa con los seminarios 2 y 3.
El seminario 2 trata:
Conceptos básicos de la herencia.
Polimorfismo.
Herencia y sistema de tipos.
Ligadura dinámica.
Métodos de la clase Object: igualdad, copia, etc.
Pruebas y herencia
El seminario 3 trata:
Clases abstractas.
Interfaces.