Herencia Amparo López Gaona Septiembre de 2008 Amparo López Gaona () Herencia Septiembre de 2008 1 / 58 Introducción Las clases no existen en el vacı́o, normalmente se relacionan unas con otras. Relación de uso. Relación de agregación/contención. Relación de herencia. Se garantiza que objetos de una clase pueden usar objetos de otra clase respetando una funcionalidad existente. Desde el punto de vista de extensibilidad es deseable poder modificar o adaptar clases existentes a nuevas necesidades, sin violar el encapsulamiento. Amparo López Gaona () Herencia Septiembre de 2008 2 / 58 Ampliación mediante herencia Hacer un programa para el mantenimiento de cuentas bancarias: cuentas de débito, cuentas con pago automático de servicios y cuentas de crédito. Con todas se permite retirar dinero, depositar dinero y conocer el saldo de la misma. Las cuentas con pago de servicio además permiten el pago automático del teléfono y las cuentas de crédito permiten comprar a crédito y consultar este crédito. Amparo López Gaona () Herencia Septiembre de 2008 3 / 58 Ampliación mediante herencia Hacer un programa para el mantenimiento de cuentas bancarias: cuentas de débito, cuentas con pago automático de servicios y cuentas de crédito. Con todas se permite retirar dinero, depositar dinero y conocer el saldo de la misma. Las cuentas con pago de servicio además permiten el pago automático del teléfono y las cuentas de crédito permiten comprar a crédito y consultar este crédito. 1 Encontrar los objetos principales. cuenta bancaria, cuenta de débito, cuenta con pago y cuenta de crédito 2 Determinar el comportamiento deseado para cada objeto. Cuenta bancaria: Crear una cuenta. Retirar dinero. Depositar dinero. Consultar saldo. Amparo López Gaona () CtaPago: Crear una cuenta. Retirar dinero. Depositar dinero. Consultar saldo. Pagar servicio. Herencia CtaCrédito: Crear una cuenta. Retirar dinero. Depositar dinero. Consultar saldo. Consultar crédito. Comprar a crédito. Septiembre de 2008 3 / 58 ... Ampliación mediante herencia Se sabe de la existencia de la clase Cuenta: public class Cuenta { private double saldo; public public public public Cuenta(double montoInicial) { ... } void retirar(double monto) { ... } void depositar(double monto) { ... } double darSaldo() { ... } } Para resolver este problema existen al menos tres posibilidades: Amparo López Gaona () Herencia Septiembre de 2008 5 / 58 ... Ampliación mediante herencia Se sabe de la existencia de la clase Cuenta: public class Cuenta { private double saldo; public public public public Cuenta(double montoInicial) { ... } void retirar(double monto) { ... } void depositar(double monto) { ... } double darSaldo() { ... } } Para resolver este problema existen al menos tres posibilidades: 1 Modificar la clase Cuenta agregando los métodos requeridos. 2 Copiar el código de Cuenta en el programa para el pago de servicios y hacer el cambio necesario. 3 Usar el mecanismo de herencia. Amparo López Gaona () Herencia Septiembre de 2008 5 / 58 ... Ampliación mediante herencia La herencia permite definir una nueva clase Cn a partir de una clase existente C . En la clase Cn se definen sólo los atributos y los métodos que difieren de los existentes en la clase C y automáticamente se incluyen los métodos y atributos de C . La clase Cn se denomina subclase o clase derivada y la clase C se conoce como superclase, clase base o clase padre. public class CuentaConServicios extends Cuenta { /** M’etodo para pagar el tel’efono ... * @param numTel - N’umero telef’onico a donde se har’a el pago * @param monto - Cantidad que debe pagarse */ public void pagarTelefono(String numTel, double monto) { retirar(monto); ... // C’odigo para pagar el tel’efono saldo += 100.00; } Herencia Septiembre de 2008 7 / 58 } Amparo López Gaona () ... Ampliación mediante herencia Un objeto de la clase derivada tiene todos los componentes de la clase base de la cual hereda más los definidos en esta misma clase, como se muestra en la siguiente tabla. Clase: Estructura: Comportamiento: Amparo López Gaona () Cuenta saldo numCuenta retirar depositar darSaldo Herencia CuentaConServicios saldo numCuenta retirar depositar darSaldo pagarTeléfono Septiembre de 2008 8 / 58 Control de acceso Inválido modificar la variable saldo de la clase Cuenta. Si se quiere tener elementos privados para todas las clases excepto para las clases derivadas, es necesario que se declaren utilizando la palabra reservada protected. Clase A private Clase B public Clase A1 Clase A2 protected public class Cuenta { protected double saldo; ... } //depositar(100.00); Amparo López Gaona () Herencia Septiembre de 2008 10 / 58 Constructores El constructor de la clase base proporciona el estado inicial para la parte heredada. Se llama cuando se crea el objeto de la clase derivada. El constructor de la clase derivada maneja la parte agregada por ésta. El constructor de una clase derivada incluye la llamada (super) a uno de los constructores de la clase base que se ejecutará antes que el cuerpo del constructor de la clase derivada. public CuentaConServicios (double montoInicial) { super(montoInicial); } En este caso el constructor de la subclase no añade nada al constructor de la superclase, pero puede hacerlo. En una jerarquı́a de herencia, la creación de un objeto de una clase derivada puede causar una serie de llamadas a constructores en la jerarquı́a. Amparo López Gaona () Herencia Septiembre de 2008 12 / 58 ... Constructores class Base { private int x, y; public Base(int a) { x = a; y = 1000;} public Base(int a, int b) { x = a; y = b;}; public void pintaB() {System.out.println(x+ " " + y);} } class Derivada extends Base{ private int z; public Derivada (int n) { z = n;} public void pintaD() {System.out.println(z);} } public class UsoHer{ public static void main(String x[]) { Base unB = new Base(1,2); Derivada unD = new Derivada(4); unB.pintaB(); unD.pintaD(); }} Amparo López Gaona () Herencia Septiembre de 2008 14 / 58 Uso de clases derivadas public class UsaCuenta { public static void main(String[] pps) { ... // Declaraciones necesarias // Solicita y valida capital inicial cuenta=new CuentaConServicios(capital); // Crea la cuenta do { ... //Muestra menu de opciones y pide seleccionar alguna switch(opcion) { case ’1’: //Retiro System.out.println("?‘Cuanto dinero quieres retirar?"); capital=io.readDouble(); cuenta.retirar(capital); break; case ’2’: //Deposito System.out.println("?‘Que cantidad deseas depositar?"); capital=io.readDouble(); cuenta.depositar(capital); break; case ’3’: // Saldo System.out.println("Tu saldo es de:$" + cuenta.obtenerSaldo()); break; case ’4’: // $Pago de telefono System.out.println("?‘Cuanto dinero vas a pagar?"); capital=io.readDouble(); cuenta.pagarTelefono(capital); System.out.println("Telefono pagado, gracias"); break; case ’9’: //Fin del programa System.out.println("*** Hasta pronto. ***"); otra = false; break; default: Amparo López Gaona () Herencia \unhbox \voidb@x \bgroupSeptiembre de 2008 16 \setbo / 58 System.out.println("Opci\penalty \@M \hskip \z@skip \let \unhbox \voidb@x Especialización mediante herencia Clase: Estructura: Cuenta saldo numCuenta Comportamiento: retirar depositar darSaldo Amparo López Gaona () Herencia CuentaDeCrédito saldo numCuenta lı́mite deuda retirar depositar darSaldo comprar obtenerValorDeuda Septiembre de 2008 17 / 58 Especialización mediante herencia public class CuentaDeCredito extends Cuenta { private double limite; // Limite de credito private double deuda; // Monto de la deuda public CuentaDeCredito (double credito) { Amparo López Gaona () Herencia Septiembre de 2008 19 / 58 Especialización mediante herencia public class CuentaDeCredito extends Cuenta { private double limite; // Limite de credito private double deuda; // Monto de la deuda public CuentaDeCredito (double credito) { super (credito); limite = credito; deuda = 0; } public double obtenerValorDeuda() { return deuda; } public boolean comprar(double monto) { if (monto > 0.0 && monto < saldo ) { deuda += monto; retirar (monto); // saldo -= monto; return true; Amparo López Gaona () Herencia Septiembre de 2008 } else { 19 / 58 Especialización mediante herencia public void retirar (double monto) { Amparo López Gaona () Herencia Septiembre de 2008 21 / 58 Especialización mediante herencia public void retirar (double monto) { if (monto >0.0 && monto <= saldo ) { double comision = monto *0.02; super.retirar(monto+comision); deuda += monto + comision; } } La clase CuentaDeCredito es más especializada que la clase Cuenta. La especialización se logra agregando atributos y métodos en la subclase. Sobreescritura = modificar el trabaja de un método en una subclase. Un método puede sobreescribirse si no es privado ni estático. Si una clase tiene la palabra final entonces no se puede derivar. Búsqueda en la jerarquı́a de abajo a arriba. Decidir cuál método aplicar a un objeto en una jerarquı́a de herencia se denomina polimorfismo. Amparo López Gaona () Herencia Septiembre de 2008 21 / 58 Jerarquı́a de Clases Las relaciones de herencia forman estructuras jerárquicas similares a un árbol. Cuenta Cuenta Atributos Atributos Métodos Métodos CuentaCon Servicios Atributos Crédito CuentaCon Servicios Crédito Inversión Atributos Atributos Atributos Atributos Métodos Métodos Métodos Métodos Métodos Nacional Atributos Métodos Internacional Nacional Internacional Atributos Atributos Atributos Métodos Métodos Métodos a) Crecimiento a lo largo Amparo López Gaona () b) Crecimiento a lo ancho Herencia Septiembre de 2008 22 / 58 ... Jerarquı́a de Clases Un objeto heredará todas las propiedades de sus padres, de los padres de sus padres, etc. public class Animal{ public void comer() {...} } public class Mamifero extends Animal { public void nacer() {...} } public class Gato extends Mamifero { public void cazarRatones() {...} public void ronrronear() {...} } Gato micifus = new Gato(); comer() nacer() cazarRatones() ronrronear() Amparo López Gaona () Herencia Septiembre de 2008 24 / 58 Ventajas de la herencia Se reduce el tiempo que toma construir nuevas aplicaciones. Las nuevas clases son pequeñas. No es necesario tener el código de las clases para extenderlas. Las aplicaciones son más fáciles de entender debido a que es menos el software nuevo. Facilita el mantenimiento, debido a que si hay algo mal sólo se tiene que reparar en un solo lugar. Amparo López Gaona () Herencia Septiembre de 2008 25 / 58 Compatibilidad Un objeto de una subclase puede usarse en cualquier lugar que un objeto de su superclase. Cuenta cta; CuentaConServicios ctaConS = new CuentaConServicios(); CuentaDeCredito ctaCred = new CuentaDeCredito(); ... cta = ctaCred; //Correcto ctaCred = cta; //Incorrecto ctaCred = (CuentaDeCredito) cta; Se puede asignar un objeto especializado a uno más general, el revés no es cierto sin una conversión explı́cita. Amparo López Gaona () Herencia Septiembre de 2008 27 / 58 ... Compatibilidad Las reglas generales para conversión explı́cita son: Si la asignación es de la forma padre = hijo, no es necesario indicar ninguna conversión. Si la asignación es de la forma hijo = padre, entonces se requiere la conversión explı́cita hijo = (ClaseHijo) padre. No se puede asignar ni hacer conversión entre clases sin relación directa. Por ejemplo, no es posible ctaConS = ctaCred;. Para evitar problemas es conveniente utilizar el operador instanceof, el cual devuelve la clase a la que pertenece el objeto. Este operador se usa, generalmente, seguido de un operador de conversión explı́cita, es decir, el nombre de una clase entre paréntesis. Amparo López Gaona () Herencia Septiembre de 2008 28 / 58 ... Compatibilidad public static void main(String[] pps ) { ... //Declaraciones Cuenta cta = null; ... //Muestra menu de opciones y pide seleccionar algun case 0: // Menu para creacion de cuenta. Puede ser: cta=new Cuenta(capital); cta=new CuentaConServicios(capital); cta = new CuentaDeCredito(5000); break; case 1: //Retiro ... cta.retirar(capital); break; case 2: //Deposito ... cta.depositar(capital); break; case 3: // Saldo Septiembre de 2008 30 / 58 Amparo López Gaona () Herencia cta.obtenerSaldo(); ... Compatibilidad case 4: // Pago de telefono if (cta instanceof CuentaConServicios) { CuentaConServicios cs = (CuentaConServicios)cta; ... // Pide datos cs.pagarTelefono(tel,capital); cta = cs; } else System.out.println("Tu cuenta no tiene habilitado este serv break; case 5: // Compra con tarjeta de credito if (cta instanceof CuentaDeCredito) { CuentaDeCredito cc = (CuentaDeCredito) cta; System.out.println("Cuanto vas a comprar"); capital=io.readDouble(); if (!cc.comprar(capital)) System.out.println("No se puede realizar la compra"); cta = cc; } else System.out.println("Tu cuenta no tiene habilitado este serv Amparo López Gaona () Herencia Septiembre de 2008 32 / 58 ... Compatibilidad Cuenta [] ... cuenta[0] cuenta[1] cuenta[2] cuentas; = new Cuenta(); = new CuentaConServicios(); = new CuentaDeCredito(); Calcular la suma del saldo de cada cuenta: double suma = 0; for (int i = 0; i < cuenta.length; i++) { suma += cuenta[i].obtenerSaldo(); } Amparo López Gaona () Herencia Septiembre de 2008 34 / 58 Polimorfismo Hasta el momento de ejecución se determina cuál método usar para calcular el sueldo. Sin polimorfismo escribir una serie de condicionales para determinar qué método llamar dependiendo de la clase a la que pertenece cada objeto. Sin polimorfismo el mantenimientose vuelve tortuoso. Con polimorfismo no es necesario cambiar este código al modificar la jerarquı́a. Amparo López Gaona () Herencia Septiembre de 2008 35 / 58 La clase Object Toda clase en Java es descendiente (directo o indirecto) de la clase Object definida en java.lang. En ella se define la estructura y comportamiento que todos los objetos deben tener. Método equals para comparar la igualdad de dos objetos. (o1.equals(o2) o1 == o2) Método getClass para obtener la clase de un objeto. Regresa un objeto de la clase Class (nombre, métodos, superclases, interfaces, etc.) System.out.println("El objeto es de la clase "+ obj.getClass().getName()); (Este no puede sobreescribirse). Método toString para convertir a cadena la representación de un objeto. System.out.println(rectangulo); Otros métodos notify, notifyAll, wait para manejo de hilos. Amparo López Gaona () Herencia Septiembre de 2008 36 / 58 ... la clase Object Amparo López Gaona () Herencia Septiembre de 2008 37 / 58 Manejo de errores public void depositar(double monto) { if (monto > 0) { saldo += monto; }} Amparo López Gaona () Herencia Septiembre de 2008 39 / 58 Manejo de errores public void depositar(double monto) { if (monto > 0) { saldo += monto; }} public void depositar(double monto) { if (monto > 0) { saldo += monto; } else { System.out.println("No es posible depositar "+ monto + " pesos." }} Amparo López Gaona () Herencia Septiembre de 2008 39 / 58 Manejo de errores public void depositar(double monto) { if (monto > 0) { saldo += monto; }} public void depositar(double monto) { if (monto > 0) { saldo += monto; } else { System.out.println("No es posible depositar "+ monto + " pesos." }} public boolean depositar(double monto) { if (monto > 0) { saldo += monto; return true; } System.out.println("No es posible depositar "+ monto + " pesos.") return false; Herencia Septiembre de 2008 39 / 58 } Amparo López Gaona () ... Manejo de errores Una excepción (exceptional event) es un evento que ocurre en cualquier momento de ejecución de un programa y que modifica el flujo normal de este. Son objetos de la clase Exception que almacenan información que se regresa en caso de que ocurra una anormalidad. Una excepción es “disparada” o activada para indicar que ocurrió una falla durante la ejecución de un método. La excepción se propaga hasta encontrar un método que la “atrape” en el cual se indica qué se debe hacer en circunstancias anómalas. Por ejemplo, imprimir un mensaje de error y no hacer nada más, terminar el programa (con o sin mensaje de error), realizar la tarea de cualquier forma, pedir la corrección de manera interactiva, etcétera. Amparo López Gaona () Herencia Septiembre de 2008 40 / 58 Jerarquı́a de excepciones La clase Exception del paquete java.lang es descendiente de la clase Object. Object Throwable Exception ClassNotFoundException IllegalAccessException NoSuchMethodException NoSuchFieldException RuntimeException IllegalArgumentException IndexOutOfBoundsException ArithmeticException ArrayStoreException ClassCastException NegativeArraySizeException NullPointerException IllegalStateException Amparo López Gaona () Herencia Septiembre de 2008 41 / 58 Estados de una excepción Las excepciones pasan por un conjunto de estados: Disparo de una excepción: Se crea un objeto Exception y el control pasa al sistema de ejecución. Principales ventajas de esta forma de tratar con errores o casos excepcionales es que para el usuario resulta imposible ignorarlos. Atrapado una excepción: Encontrar un método que responda a la excepción. Finalizar: Si no se encuentra método se termina la ejecución del programa. En caso contrario se recupera de la excepción. Amparo López Gaona () Herencia Septiembre de 2008 42 / 58 Disparo de excepciones Disparar una excepción es la forma más efectiva para indicar que no es posible atender la petición del objeto que envı́a el mensaje. public void depositar(double monto) throws Exception { if (monto < 0) { throw Exception("Deposito incorrecto"); } saldo += monto; } public double retirar(double monto) throws Exception { if (monto <= 0) { throw new Exception("La cantidad a retirar debe ser positiva"); } if (saldo < monto) { saldo -= 500; throw new Exception("Fondos insuficientes para realizar el retir } saldo -= monto; return monto; Septiembre de 2008 44 / 58 Amparo López Gaona () Herencia ... Disparo de excepciones public Cuenta(double saldoInicial) { if (saldoInicial < 2500) { throw new IllegalArgumentException("El monto inicial es menor a } saldo = saldoInicial; } Amparo López Gaona () Herencia Septiembre de 2008 46 / 58 Manejo de excepciones El manejador de excepciones puede estar en cualquier parte del programa y tiene la siguiente forma: try { instrucciones } catch (..Exception e) { instrucciones } finally { instrucciones } Amparo López Gaona () Herencia Septiembre de 2008 47 / 58 ... Manejo de excepciones public class PruebaExcepcionesCuenta { static public void main(String pps[]) { double monto; InOut io = new InOut(); try{ System.out.println("Proporciona el monto inicial"); monto = io.readDouble(); Cuenta cta = new Cuenta(monto); ... System.out.println("Proporciona el monto del retiro"); monto =io.readDouble(); cta.retirar(monto); System.out.println("El saldo actual es"+cta.daSaldo()); } catch (Exception e) { System.out.println(e); } System.out.println("Fin del programa"); } Septiembre de 2008 Herencia } Amparo López Gaona () 49 / 58 ... Manejo de excepciones Ejecución con un monto inicial no numérico. Proporciona el monto inicial a50 java.lang.NumberFormatException: a50 Fin del programa Amparo López Gaona () Herencia Septiembre de 2008 51 / 58 ... Manejo de excepciones Ejecución con un monto inicial no numérico. Proporciona el monto inicial a50 java.lang.NumberFormatException: a50 Fin del programa Ejecución del programa con un monto inicial incorrecto. Proporciona el monto inicial 90 java.lang.IllegalArgumentException: El monto inicial es menor a$ Fin del programa Amparo López Gaona () Herencia Septiembre de 2008 51 / 58 ... Manejo de excepciones Ejecución con un monto inicial no numérico. Proporciona el monto inicial a50 java.lang.NumberFormatException: a50 Fin del programa Ejecución del programa con un monto inicial incorrecto. Proporciona el monto inicial 90 java.lang.IllegalArgumentException: El monto inicial es menor a$ Fin del programa Intento de retirar una cantidad mayor a la disponible. Proporciona el monto inicial 3000 Proporciona el monto del retiro 5000 java.lang.Exception: Fondos insuficientes para realizar el retir Fin del programa Amparo López Gaona () Herencia Septiembre de 2008 51 / 58 ... Manejo de excepciones Ejecución sin errores. Proporciona el monto inicial 3000 Proporciona el monto del retiro 1000 Fin del programa Amparo López Gaona () Herencia Septiembre de 2008 53 / 58 Creación de excepciones propias public class ExcepcionBancaria extends Exception { public ExcepcionBancaria() { super(); } public ExcepcionBancaria(String s) { super(s); } } Amparo López Gaona () Herencia Septiembre de 2008 55 / 58 Creación de excepciones propias public class ExcepcionBancaria extends Exception { public ExcepcionBancaria() { super(); } public ExcepcionBancaria(String s) { super(s); } } public void depositar(double monto) throws ExcepcionBancaria { if (monto <= 0) throw new ExcepcionBancaria("No es posible realizar un deposito saldo += monto; } public void retirar(double monto) throws ExcepcionBancaria { if (monto <= 0 || monto > disponible) { throw new ExcepcionBancaria("No es posible hacer un retiro negat } saldo -= monto; Amparo López Gaona () Herencia Septiembre de 2008 55 / 58 Recuperación de excepciones Creación de una cuenta, verificando que el monto inicial sea correcto. OK = false; intentos = 0; do { try { System.out.println("Proporciona el monto inicial"); monto = io.readDouble(); Cuenta cta = new Cuenta(monto); OK = true; } catch (Exception e) { System.out.println(e); intentos ++; } } while (!OK && intentos < 5); ... Amparo López Gaona () Herencia Septiembre de 2008 57 / 58 Ventajas Entre las ventajas que se tienen de trabajar con excepciones se tienen las siguientes: 1 Permiten separar el código para manejo de error, del código “normal”. Aunque esto no evita que se deba especificar cuáles son los posibles errores y qué hacer en caso de que ocurran. 2 Se pueden agrupar tipos de errores y diferenciar errores. Por ejemplo, todos los que pueden ocurrir al trabajar con arreglos, con archivos, etc., pero también se puede trabajar con cada uno por separado. 3 Al crear excepciones propias se están creando códigos de error propios y son manejados por Java de manera idéntica a sus propias excepciones. Amparo López Gaona () Herencia Septiembre de 2008 58 / 58