Algoritmos y Programación III 2. Herencia, polimorfismo, interfaces Carlos Fontela, 2005
Temario Reutilización con composición y con herencia Redefinición Cuándo usar herencia y cuándo composición Polimorfismo y vinculación tardía Métodos virtuales Métodos y clases abstractas Interfaces Clases internas
Reutilización Dos acepciones Usar clases en una aplicación diferente a que motivó su primer uso Extensión: Usar clases como base para la creación de otras nuevas con comportamiento parecido No necesitamos el código fuente
Mecanismos de abstracción clasificación (individuo-especie) Lassie - perro agrupación (entre individuos) Auto - rueda generalización (entre especies) Lápiz – herramienta de escritura En OO instanciación (objeto-clase) agregación (objeto-objeto) herencia (clase-clase)
Herencia (I) En Java, para indicar herencia public class Elipse extends Figura { ... } Elipse tiene, por lo menos: los mismos atributos de Figura los mismos métodos de Figura puede agregar atributos y métodos puede redefinir métodos ¡Reutilización de clases compiladas!
Herencia (II) “Casteo” hacia arriba automático Otro caso Elipse e = new Elipse(); Figura f = e; // válido y seguro // e = f; inválido e inseguro Otro caso public void p (Figura x) { … } // … luego será invocado: p(e); // e es de tipo Elipse Los llamamos “objetos polimorfos” Parecen una cosa pero son otra f es una Elipse aunque se la trate como Figura
Herencia (III) O colecciones polimorfas Pero public Figura[ ] v; // luego… v[1] = unaElipse; v[2] = unCirculo; v[3] = unTrianguloRectangulo; Pero Estamos perdiendo información del objeto Cada v[i] sólo va a tener disponibles los atributos y métodos públicos de Figura Ya volveremos sobre esto
Más sobre herencia en Java Jerarquía de raíz única Clase Object Todo se puede transformar en un Object Atributos y métodos comunes a todas las clases Otras importantes consecuencias Posibilidad de evitar la herencia Declaramos la clase como “final”: Ejemplo: String public final class String {...}
Más sobre visibilidad Atributos y métodos con visibilidad “de paquete” (por defecto) Sólo pueden verse dentro de su mismo paquete Atributos y métodos protegidos (protected) Son visibles sólo para las clases descendientes y las del propio paquete Poco uso; riesgos Clases con visibilidad “de paquete” Sólo se pueden usar dentro de su paquete No hay clases privadas ni protegidas
Constructores Los constructores no se heredan Receta 1 Receta 2 Cada clase debe tener el suyo Receta 1 Llamar al constructor del ancestro al principio del constructor propio public Derivada() { super(); ... } Automático con constructores por defecto Receta 2 Llamar al constructor de un objeto contenido desde el constructor del objeto compuesto Esto nunca es automático
Redefinición (I) Se puede volver a definir un método en una clase descendiente:
Redefinición (II) Debe preservar la semántica (significado) Obligatoria Si la implementación debe ser diferente Caso de imprimirAtributos Optativa Razones, en general, de eficiencia Caso de longitud de Elipse Los métodos deben tener la misma signatura
Redefinición (III) Los métodos privados no pueden ser redefinidos Se estaría definiendo uno nuevo Posibilidad de evitar la redefinición Métodos “final” public final void m() {...} No se puede redefinir un método haciéndolo más privado Sobrecarga y redefinición Redefinición se hace con la misma signatura Si no, es sobrecarga
Composición vs. Herencia Herencia: relación “es un” Composición/agregación: “contiene” “hace referencia” “es parte de” Mito: en POO todo es herencia Mal ejemplo: Stack en Java 1.0/1.1 ¡una pila no es un vector! Herencia si se va a reutilizar la interfaz Stack es un mal ejemplo
Herencia múltiple: no en Java Las clases dejaron de ser excluyentes
Problemas de la herencia Difícil construir buenas jerarquías de tamaño mediano a grande. Dificultad de un buen manejo de toda la jerarquía. Mayores costos de aprendizaje. Importante Buen diseño de clases y paquetes Buena documentación puede ser tipo javadoc
Polimorfismo Objetos de distintas clases de una misma familia entienden los mismos mensajes Igual semántica o significado Implementaciones diferentes Un mismo mensaje puede provocar la invocación de métodos distintos Vinculación tardía Se retarda la decisión sobre el tipo del objeto hasta el momento en que vaya a ser utilizado el método
Métodos virtuales (I) public void dibujarTodas { for (int i = 0; i < cantFiguras; i++) v[i].dibujar(); } public void mover (int x, int y) { borrar(); nuevasCoordenadas(x,y); dibujar(); }
Métodos virtuales (II) // llama al dibujar de Elipse: unaElipse.mover(); // llama al dibujar de Triangulo: unTriangulo.mover(); // llama al dibujar de la clase de cada v[i]: v.dibujarTodas(); // porque dibujar es virtual En Java la “virtualidad” se da por defecto En otros lenguajes no Métodos privados y final no son virtuales
Métodos virtuales (III) Los métodos virtuales agregan ineficiencias Pero garantizan reutilización Eliminar la “virtualidad” sólo si se demuestra que no se van a redefinir y la presunta ineficiencia Un método debe ser virtual sí o sí cuando se lo redefinirá y es llamado desde: Un método no redefinido en una clase ancestro. Un método de una clase que contenga como atributo algún objeto de una clase ancestro
Caso típico estándar En Object toString, redefinible System.out.println usa toString() Si quiero que un objeto sea imprimible, debo redefinir toString: En CtaCte public String toString() { return nro+titular+Double.toString(saldo); } Luego... CtaCte cc = new CtaCte(12,”Juan”); System.out.println(cc);
Métodos abstractos Nunca va a ser invocado No tiene implementación Caso del dibujar de Figura Pero se necesita para el compilador No tiene implementación Corolarios Debe redefinirse Debe ser virtual En Java: public void abstract dibujar();
Clases abstractas No tienen instancias Caso de Figura Pero pueden tener constructor ¡que no debe ser llamado nunca! Generalizan estructura y comportamiento de varias clases Caso del método mover O crean una familia En Java: Si se declara “abstract” Si tiene métodos abstractos En este caso, declarar “abstract”
En UML
Interfaces (I) Nombre confuso Son grupos de métodos sin implementar
Interfaces (II) Son como clases Ejemplo Uso Corolario Abstractas Todos los métodos abstractos Ejemplo public interface Imprimible { void imprimir(); // público y abstracto por defecto } Uso public class Vector extends Matriz implements Imprimible {...} Corolario Si una clase declara implementar una interfaz y no define uno de sus métodos es abstracta
Interfaces (III) Una clase puede implementar varias Ojo con los conflictos de nombres Hay herencia entre interfaces ¡Variables cuyo tipo es una interfaz! Imprimible p = new Fecha(20,6,1964); Imprimible[ ] v = new Imprimible[3]; v[0] = new Fecha(20,1,2000); v[1] = new Vector(); v[2] = new Fecha(8,6,2002); … for (int i=0; i < 3; i++) v[i].imprimir(); Ojo que no estoy instanciando la interfaz sino una clase
Interfaces y polimorfismo
Interfaces predefinidas Caso de Comparable En java.lang.Comparable interface Comparable { int compareTo (Object o); } Devuelve valores <0, 0 o >0 ¡Muy útil en colecciones! Hay otras predefinidas Comparator, Serializable, Cloneable, etc.
Uso de Comparable public class Fraccion implements Comparable { private int n; private int d; // otros métodos public int compareTo(Object otro) { Fraccion otroR = (Fraccion)otro; if (n * otroR.d > d * otroR.n) return 1; else if (n * otroR.d < d * otroR.n) return -1; else return 0; }
Clases internas Pueden utilizar métodos y atributos privados de su clase externa (sin relación de herencia). Pueden ser descendientes de cualquier clase visible de la clase externa. En una clase descendiente de la externa se puede declarar una clase interna que descienda de la interna del ancestro. Se usan poco.
Clases dentro de métodos public class Externa { //... public void m() { class Interna extends Base { // ... } Interna i = new Interna();
Clases internas anónimas public class Externa { //... public void m() { // ... return new Base() { // declaración de la clase anónima ... }
Resumen Herencia para relaciones “es un” Composición para relaciones “es parte de” Redefinición permite cambiar implementación manteniendo la semántica Métodos virtuales aseguran reutilización al máximo Métodos y clases abstractas son de uso bien concreto y habitual Interfaces son grupos de métodos sin implementar Pero se usan casi como clases
Qué sigue Excepciones Colecciones RTTI y reflexión Un poco más de UML Manejo de problemas en tiempo de ejecución Colecciones RTTI y reflexión Información sobre tipos en tiempo de ejecución Un poco más de UML
Muchas Gracias. Carlos Fontela, 2005