Upload
utn
View
439
Download
0
Embed Size (px)
Citation preview
Tema 3 – Herencia
Programación Orientada a Objetos
Curso 2010/2011
Primera parte. Herencia en Java
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.
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.
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
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.
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
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
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.
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.
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;
}…
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;
}
}
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.
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() { … }
}
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()).
Programación Orientada a Objetos 15Curso 2010/2011
Jerarquía de herencia
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(…)
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;
}
…
}
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.
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.
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.
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.
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();
}
…
}
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.
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);
}
}
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);
}
}
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.
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) {…}
}
Programación Orientada a Objetos 28Curso 2010/2011
Jerarquía de herencia
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.
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.
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.
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.
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
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().
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).
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}
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;
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.
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.
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;
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);
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
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!
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
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();
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.
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?
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í).
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.
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();
}
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();
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.
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.
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: Integerpara 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;
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.
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.
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.
Programación Orientada a Objetos 58Curso 2010/2011
Copia superficial de Cuenta
� Copia superficial:
� Aliasingincorrecto al compartir las últimas operaciones.
� No deberían tener el mismo código
J. Gomez
87654321
nombre
dni
Objeto Persona
-500010000
123456
100000
titular
codigo
saldo
ultOper
cuenta
123456
100000
titular
codigo
saldo
ultOper
copia
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
-500010000
123456
100000
titular
codigo
saldo
ultOper
cuenta
123456
100000
titular
codigo
saldo
ultOper
copia
-500010000
J. Gomez
87654321
Programación Orientada a Objetos 60Curso 2010/2011
Copia correcta de Cuenta
� Copia adaptada: cumple los requisitos de la aplicación
J. Gomez
87654321
-500010000
123456
100000
titular
codigo
saldo
ultOper
cuenta
326457
100000
titular
codigo
saldo
ultOper
copia
-500010000
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;
}}
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
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;
}
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?
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);}
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
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.
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 (…);
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).
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);
}
}
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().
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);
}
}
}
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) {
…
}
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.
Programación Orientada a Objetos 75Curso 2010/2011
Jerarquía de herencia
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;
}
}
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.
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();
}
Programación Orientada a Objetos 79Curso 2010/2011
Jerarquía de herencia
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?
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.
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
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.
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);}
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);
}
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 {
…@Overridepublic boolean amortizar(double cantidad) {
if (cantidad > capital) return false;
capital = capital - cantidad;return true;
}}
Programación Orientada a Objetos 87Curso 2010/2011
Interfaces
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);
}
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.
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.
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.