Conceptos de Herencia Avanzada H. Tejeda Abril 2016
´Indice 1. Creaci´ on y uso de clases abstractas
1
2. Enlazado din´ amico de m´ etodos
5
3. Creaci´ on de arreglos de objetos subclase
7
4. Clase Object y sus m´ etodos
8
5. Herencia para un buen dise˜ no de software
13
6. Creaci´ on y uso de interfaces
14
7. Creaci´ on y uso de paquetes
19
1.
Creaci´ on y uso de clases abstractas
Cuando se usa una clase como una base para crear clases hijas extendidas, las clases hija son m´as espec´ıficas que su padre. Cuando se crea una clase hija, esta hereda todos los atributos generales que se requieren; as´ı solo se deben crear los atributos nuevos, m´as espec´ıficos. Por ejemplo, una clase EmpleadoAsalariado y una EmpleadoPorHoras son m´as espec´ıficas que una Empleado. Las clases pueden heredar atributos generales Empleado, tales como n´ umero de empleado, pero estas pueden agregar los atributos espec´ıficos, tales como sus m´etodos de c´alculo de pago.
1
Observar que una superclase contiene las caracter´ısticas que son compartidas por sus subclases. Los atributos de la clase Perro son compartidas por cada Chihuahua o Labrador. Las subclases son ejemplos m´as espec´ıficos del tipo superclase; estas agregan m´as caracter´ısticas a las caracter´ısticas generales compartidas. Inversamente, cuando se examina una subclase, se ve que su padre es m´as general y menos espec´ıfico; Animal es m´as general que Perro. Nota. Los t´erminos clase base, superclase, y padre son equivalentes. De igual forma, los t´erminos clase derivada, subclase e hija son equivalentes. Una clase hija contiene todos los miembros de su padre, ya sea que estos miembros sean public, protected, o private. Sin embargo, un objeto hijo no puede acceder directamente un miembro private heredado de un padre.
Una clase concreta es una de la cual se pueden instanciar objetos. En algunos casos un clase es muy general que nunca se intenta crear alguna instancia espec´ıfica de la clase. Por ejemplo, nunca se podr´ıa querer crear un objeto que es “s´olo” un Empleado; cada Empleado es m´as especif´ıcamente un EmpleadoAsalariado, EmpleadoPorHoras, o EmpleadoPorContrato. Una clase tal como Empleado que se crea solamente para ser extendida no es una clase concreta; es una clase abstracta. Se crean clases final si no se quiere que otras clases puedan extenderlas. Las clases que se declaran para ser abstract son lo opuesto; su u ´nico prop´osito es crearlas para permitir que otras clases las puedan extender. Si se intenta instanciar un objeto de una clase abstracta, se recibe un mensaje de error del compilador indicando que se ha cometido un InstantiationError. Se usa la palabra reservada abstract cuando se declara una clase abstracta. Nota. En los diagramas de clase, por convenci´on, cuando se muestran clases abstractas y m´etodos, sus nombres aparecen en it´alicas.
En el dise˜ no de una clase abstracta se pueden incluir dos tipos de m´etodos: M´etodos no abstractos, como aquellos que se han creado en cualquier clase, est´an implementados en la clase abstracta y son heredados simplemente por sus hijas. M´ etodos abstractos no tienen cuerpo y deben ser implementados en las clases hija. Las clases abstractas usualmente contienen al menos un m´etodo abstracto. Cuando se crea un m´etodo abstracto, se pone la palabra reservada abstract y el resto de la cabecera del m´etodo, incluyendo el tipo del m´etodo, nombre, y par´ametros. Sin embargo, la declaraci´on termina ah´ı: no se ponen llaves, ni ninguna sentencia, solo un punto y coma al final de la declaraci´on. Si se crea un m´etodo vac´ıo dentro de una clase abstracta, el m´etodo es un m´etodo abstracto a´ un si no se uso expl´ıcitamente la palabra reservada abstract cuando se defini´o el m´etodo, pero los programadores frecuentemente incluyen abstract por claridad. Si se declar una clase para que sea abstracta, sus m´etodos pueden ser abstractos o no, pero 2
si se declara un m´etodo para ser abstracto, tambi´en se deber´a declarar la clase para ser abstracta. Cuando se crea una subclase que hereda un m´etodo abstracto, se escribe un m´etodo con la misma firma. Se requiere codificar un m´etodo subclase que anule cada m´etodo superclase abstracto vac´ıo que es heredado. O el m´etodo de la clase hija deber´a ser por s´ı mismo abstracto, o se deber´a dar un cuerpo, o implementaci´on, para el m´etodo heredado. Suponer que se quieren crear clases para representar diferentes animales, tales como Perro o Vaca. Se puede crear un clase gen´erica abstracta llamada Animal para dar campos de datos gen´ericos, tales como el nombre del animal, una sola vez. Un Animal es gen´erico, pero todos los animales espec´ıficos pueden hacer un sonido; el sonido de un animal difiere de otro animal. Si se codifica un m´etodo vac´ıo hablar() en la clase abstracta Animal, se requerir´a que todas las subclases Animal futuras codifiquen el m´etodo hablar() de acuerdo a la subclase. El codigo 1 muestra la clase abstracta Animal conteniendo un campo de datos para el nombre, el m´etodo accesor y mutador para este campo, y un m´etodo abstracto hablar(). 1 2 3 4 5 6 7 8 9 10
public abstract c l a s s Animal { private S t r i n g nombre ; public abstract void h a b l a r ( ) ; public S t r i n g getNombre ( ) { return nombre ; } public void setNombre ( S t r i n g nombreAnimal ) { nombre = nombreAnimal ; } }
C´odigo 1: La clase Animal astracta. La clase Animal, c´odigo 1, es declarada como abstracta. No se puede crear una clase en la cual se declare un objeto Animal como en: Animal mascota = new Animal("Manchas"); porque no se podr´a compilar. Animal es una clase abstracta, as´ı que objetos Animal no pueden existir. Se crea una clase abstracta tal como Animal s´olo para extenderla. Como un perro es un animal, se puede crear la clase Perro como hija de la clase Animal. El c´odigo 2 muestra la clase Perro que extiende la clase Animal.
3
1 2 3 4 5
public c l a s s Perro extends Animal { public void h a b l a r ( ) { System . out . p r i n t l n ( ”¡Guau , guau ! ” ) ; } }
C´odigo 2: La clase Perro. El m´etodo hablar() es requerido en la clase Perro porque se quieren crear objetos Perro, la clase padre Animal tiene un m´etodo abstracto hablar(). Se puede codificar cualquier sentencia que se quiera dentro del m´etodo hablar(), pero el m´etodo deber´a existir. Si no se quieren crear objetos Perro pero se quiere que la clase Perro sea padre m´as tarde, entonces la clase Perro deber´a ser abstracta tambi´en. Para este caso, se escribe el c´odigo para el m´etodo Hablar() dentro de las subclases de Perro. Si Animal es una clase abstracta, no se puede instanciar un objeto Animal; pero si Perro es una clase concreta, se pueden instanciar objetos de esta clase, haciendo algo como lo siguiente: Perro mascota = new Perro("Manchas"); Entonces, cuando se codifica mascota.hablar();, el m´etodo hablar() Perro correcto se ejecuta. Las clases Vaca y Serpiente, c´odigos 3 y 4 respectivamente, tambi´en heredan de la clase Animal e implementan el m´etodo hablar(). 1 2 3 4 5
public c l a s s Vaca extends Animal { public void h a b l a r ( ) { System . out . p r i n t l n ( ”¡Muu, muu ! ” ) ; } }
C´odigo 3: La clase Vaca. 1 2 3 4 5
public c l a s s S e r p i e n t e extends Animal { public void h a b l a r ( ) { System . out . p r i n t l n ( ”¡ S s s s s s ! ” ) ; } }
C´odigo 4: La clase Serpiente. La aplicaci´on UsaAnimales, c´odigo 5, crea los objetos Perro, Vaca y Serpiente, cada uno es un Animal con acceso a los m´etodos getNombre() y setNombre() de la clase Animal, y cada uno usa su m´etodo hablar() apropiadamente.
4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public c l a s s UsaAnimales { public s t a t i c void main ( S t r i n g [ ] a r g s ) { Perro miPerro = new Perro ( ) ; Vaca miVaca = new Vaca ( ) ; S e r p i e n t e m i S e r p i e n t e = new S e r p i e n t e ( ) ; miPerro . setNombre ( ”Mi p e r r o Pete ” ) ; miVaca . setNombre ( ”Mi vaca Vani ” ) ; m i S e r p i e n t e . setNombre ( ”Mi s e r p i e n t e S e r g e ” ) ; System . out . p r i n t ( miPerro . getNombre ( ) + ” d i c e ” ) ; miPerro . h a b l a r ( ) ; System . out . p r i n t ( miVaca . getNombre ( ) + ” d i c e ” ) ; miVaca . h a b l a r ( ) ; System . out . p r i n t ( m i S e r p i e n t e . getNombre ( ) + ” d i c e ” ) ; miSerpiente . hablar ( ) ; } }
C´odigo 5: La aplicaci´on UsaAnimales. Al ejecutar la aplicaci´on UsaAnimales se observa que cada objeto al usar el m´etodo padre getNombre() y el m´etodo hijo hablar() se producen diferentes salidas. Usando el mismo nombre de m´etodo para indicar diferentes implementaciones es polimorfismo. Usando polimorfismo, un nombre de m´etodo causa acciones diferentes y apropiadas para tipos diversos de objetos.
2.
Enlazado din´ amico de m´ etodos
Cuando se crea una superclase y una subclase o m´as, cada objeto de cada subclase “es un” objeto superclase. Cada EmpleadoAsalariado “es un” Empleado; cada Perro “es un” Animal. Como cada objeto subclase “es un” miembro superclase, se pueden convertir objetos subclases a objetos superclase. Cuando una superclase es abstracta, no se pueden instanciar objetos de la superclase; sin embargo, se puede crear indirectamente una referencia a un objeto abstracto superclase. Una referencia no es un objeto, pero apunta a una direcci´on de memoria. Cuando se crea una referencia, no se usa la palabra reservada new para crear un objeto concret; en vez de esto, se crea una variable con nombre la cual guardar´a la direcci´on de memoria de un objeto concreto. As´ı, aunque una referencia a un objeto superclase abstracto no es concreto, se puede guardar una referencia objeto subclase concreto. Por ejemplo, si se crea una clase Animal y varias subclases, tales como Perro, Vaca, y Serpiente, c´odigos 1, 2, 3, y 4 respectivamente, se puede crear una aplicaci´on conteniendo una variable referencia Animal gen´erica en la cual se pueda asignar cualquiera de los objetos hijo Animal concreto. El c´odigo 6 muestra la aplicaci´on ReferenciaAnimal. En la aplicaci´on 5
la variable refAnimal es un tipo de Animal. No se puede crear un objeto superclase Animal; en vez de esto, objetos Vaca y Perro son creados usando la palabra reservada new. Cuando el objeto Vaca es asignado a la referencia Animal, la llamada del m´etodo refAnimal.hablar() muestra “¡Muu, muu!”; cuando el objeto Perro es asignado a la referencia Animal, la llamada al m´etodo muestra “¡Guau, guau!”. Cuando se asigna una variable o una constante de un tipo a una variable de otro tipo es llamado promoci´on, conversi´on impl´ıcita, o upcasting. 1 2 3 4 5 6 7 8 9
public c l a s s R e f e r e n c i a A n i m a l { public s t a t i c void main ( S t r i n g [ ] a r g s ) { Animal r e f A n i m a l ; r e f A n i m a l = new Vaca ( ) ; refAnimal . hablar ( ) ; r e f A n i m a l = new Perro ( ) ; refAnimal . hablar ( ) ; } }
C´odigo 6: Aplicaci´on ReferenciaAnimal. La aplicaci´on del c´odigo 6 muestra que usando una referencia polim´orficamente permite extender una clase base y usar objetos extendidos cuando un tipo clase base es esperado. Por ejemplo, si un m´etodo espera un Animal se podr´ıa pasar un objeto Perro o Vaca. Por lo anterior, todos los m´etodos escritos para aceptar argumentos superclase pueden tambi´en ser usados con sus hijos, una caracter´ıstica que ahorra trabajo a los creadores de clases hijo. Nota. Se puede usar la palabra reservada instanceof para determinar si un objeto es una instancia de cualquier clase en su jerarqu´ıa.
En la aplicaci´on del c´odigo 6 se muestra el comportamiento polim´orfico. La misma sentencia, refAnimal.hablar(), se repite cada vez que se asigna a animalRef un nuevo tipo de animal. Cada llamada al m´etodo hablar() produce salidas diferentes. Cada referencia “escoge” el m´etodo correcto hablar(), basado en el tipo de animal referido. Este comportamiento flexible es m´as u ´til cuando se pasan referencia a m´etodos. En Java todas las llamadas a m´etodos de instancia son m´etodos virtuales por defecto, ya que el m´etodo usado se determina cuando el programa se ejeuta, porque el tipo del objeto usado podr´ıa no ser conocido hasta la ejecuci´on del m´etodo. La habilidad para seleccionar el m´etodo subclase correcto dependiendo del tipo de argumento es conocido como enlazado din´ amico del m´ etodo. Cuando la aplicaci´on se ejecuta, el m´etodo correcto es conectado a la aplicaci´on basado en el contexto actual y cambiante. El enlazado din´amico del m´etodo tambi´en es conocido como enlazado tard´ıo del m´ etodo. Lo opuesto es el enlazado est´ atico del m´ etodo. En Java, m´etodos de instancia, aquellos que reciben una referencia this, usan enlazado din´amico; m´etodos de clase usan enlazado est´atico. Enlazado din´amico hace los programas flexibles; pero, enlazado est´atico es m´as r´apido.
6
2.1.
Uso de superclase como tipo de par´ ametro del m´ etodo
El enlazado din´amico es m´as u ´til cuando se crea un m´etodo que tiene uno, o o m´as par´ametros, que podr´ıan ser uno de varios tipos. Por ejemplo, en la cabecera del m´etodo animalHablando() de la aplicaci´on DemoAnimalHablando, c´odigo 7, acepta cualquier tipo de argumento Animal. El m´etodo puede ser usado con programas que contengan objetos Perro, Vaca, o cualquier otro que descienda de Animal. En la aplicaci´on primero se pasa un Perro y luego una Vaca al m´etodo. Cuando se ejecuta la aplicaci´on DemoAnimalHablando se verifica que el m´etodo trabaja correctamente no importando el tipo de Animal descendiente recibido. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public c l a s s DemoAnimalHablando { public s t a t i c void main ( S t r i n g [ ] a r g s ) { Perro p e r r o = new Perro ( ) ; Vaca vaca = new Vaca ( ) ; p e r r o . setNombre ( ”Manchas” ) ; vaca . setNombre ( ” Manchotas ” ) ; animalHablando ( p e r r o ) ; animalHablando ( vaca ) ; } public s t a t i c void animalHablando ( Animal animal ) { System . out . p r i n t l n ( ”Vengan t o d o s . ” ) ; System . out . p r i n t l n ( ”¡Vean a l s o r p r e n d e n t e animal h a b l a n t e ! ” ) ; System . out . p r i n t l n ( animal . getNombre ( ) + ” d i c e ” ) ; animal . h a b l a r ( ) ; System . out . p r i n t l n ( ” Aplausos ” ) ; } }
C´odigo 7: Aplicaci´on DemoAnimalHablando.
3.
Creaci´ on de arreglos de objetos subclase
Cada elemento del arreglo deber´a ser del mismo tipo de dato, el cual puede ser primitivo, tipo incorporado, o un tipo basado en una clase m´as compleja. Cuando se crea un arreglo en Java, no se est´an construyendo objetos. En vez de esto, se est´a reservando espacio para referencia a objetos. A pesar de que es m´as conveniente referirse a “un arreglo de objetos”, cada arreglo de objetos es realmente un arreglo de referencias de objetos. Cuando se crea un arreglo de referencias superclases, este puede guardar referencias subclases, no importando que la superclase sea abstracta o concreta. Por ejemplo, un arreglo Animal podr´ıa contener elementos individuales como objetos Perro, Vaca, o Serpiente. Cada subclase Animal tiene acceso al m´etodo hablar() y se puede manipular el arreglo de objetos superclase y llamar el m´etodo apropiado para cada miembro subclase.
7
La siguiente sentencia crea un arreglo de tres referencias Animal: Animal[] refAnimal = new Animal[3]; La sentencia aparta memoria suficiente para tres objetos Animal llamados refAnimal[0], refAnimal[1], y refAnimal[2]. La sentencia no instancia alg´ un Animal, adem´as no se puede hacer porque Animal es clase abstracta. Si se instancian objetos de subclases Animal, se pueden poner las referencias a esos objetos en el arreglo Animal, como se muestra en la aplicaci´on DemoArregloAnimales, c´odigo 8. Cuando se ejecuta DemoArregloAnimales se muestra que cada una de las tres referencias del arreglo accede al m´etodo apropiado hablar(). 1 2 3 4 5 6 7 8 9 10
public c l a s s DemoArregloAnimales { public s t a t i c void main ( S t r i n g [ ] a r g s ) { Animal [ ] r e f A n i m a l = new Animal [ 3 ] ; r e f A n i m a l [ 0 ] = new Perro ( ) ; r e f A n i m a l [ 1 ] = new Vaca ( ) ; r e f A n i m a l [ 2 ] = new S e r p i e n t e ( ) ; for ( int i =0; i