Sei sulla pagina 1di 25

4.

1 Definicion de polimorfismo
Polimorfismo
El polimorfismo es un concepto de la programacin orientada a objetos que nos
permite programar en forma general, en lugar de hacerlo en forma especfica.
En general nos sirve para programar objetos con caractersticas comunes y que
todos estos compartan la misma superclase en una jerarqua de clases, como si
todas fueran objetos de la superclase. Esto nos simplifica la programacin.
Recuerde el ejemplo del ecosistema, en donde todos los objetos de las
distintas especies heredaban de una superclase llamada Animal, que brindaba la
informacin general de cualquier animal, independiente de su especie. Sin
embargo, cada especie hace un uso particular de cada uno de los mtodos u
operaciones de la clase Animal. El mtodo comer() no se ejecutar de la misma
manera en un Len() o en un Pavo(). Lo mismo ocurre para mtodos moverse()
en objetos de tipo Tiburn() o Gallina(), aunque todas las especies realicen
estos mtodos. A la sobrescritura o implementacin especfica de mtodos es
la clave del polimorfismo.
Para poner en prctica se har un ejemplo bastante sencillo. Se har una
librera de clases que represente figuras tridimensionales y bidimensionales, y
su respectiva jerarqua de clases. Las clases deben ser capaces de tener
funcionamiento bastante bsico, como obtener reas, volmenes y permetros
de la figura correspondiente.

La representacin de la jerarqua sera como sta:

La superclase de dicha jerarqua podra ser muy parecida a sta:


public abstract class figura {
protected String nombre;
protected int color;

protected int grosorBorde;


public String getNombre(){
return this.nombre;
}
public void setNombre(String n){
this.nombre=n;
}
public int getColor(){
return this.color;
}
public void setColor(int c){
this.color=c;
}
public int getGrosorBorde(){
return this.grosorBorde;
}
public void setGrosorBorde(int g){
this.grosorBorde=g;
}
public abstract void dibujar();
}
Las siguientes clases en el nivel de la jerarqua podran quedar muy parecidas a
stas:
public abstract class figura2D extends figura {
public abstract int calcularArea();
public abstract int calcularPerimetro();
}

public abstract class figura3D extends figura {


public abstract int calcularVolumen();
}
Se le pide que forme las clases de la parte inferior de la jerarqua y que
representaran los objetos a instanciarse.
Adems, debe de realizar una implementacin de esta librera, en donde el
usuario pueda crear nuevas figuras y que stas se almacenen en un arreglo de
figuras.
Enlace dinmico
En el lenguaje C, los identificadores de la funcin estn asociados siempre a
direcciones fsicas antes de la ejecucin del programa, esto se conoce como
enlace temprano o esttico. Ahora bien, el lenguaje C++ y Java permiten
decidir a que funcin llamar en tiempo de ejecucin, esto se conoce como
enlace tardo o dinmico. Vamos a ver un ejemplo de ello.
Podemos crear un array de la clase base Figura y guardar en sus elementos los
valores devueltos por new al crear objetos de las clases derivadas.
Figura[] fig=new Figura[4];
fig[0]=new Rectangulo(0,0, 5.0, 7.0);
fig[1]=new Circulo(0,0, 5.0);
fig[2]=new Circulo(0, 0, 7.0);
fig[3]=new Rectangulo(0,0, 4.0, 6.0);
La sentencia
fig[i].area();
a qu funcin area llamar?. La respuesta ser, segn sea el ndice i. Si i es
cero, el primer elemento del array guarda una referencia a un objeto de la
clase Rectangulo, luego llamar a la funcin miembro area de Rectangulo. Si ies
uno, el segundo elemento del array guarda una referencia un objeto de la
clase Circulo, luego llamar tambin a la funcin area de Circulo, y as
sucesivamente. Pero podemos introducir el valor del ndice i, a travs del
teclado, o seleccionando un control en un applet, en el momento en el que se
ejecuta el programa. Luego, la decisin sobre qu funcin area se va a llamar se
retrasa hasta el tiempo de ejecucin.

4,2 Clases Abstractas

Concepto
Hay ocasiones, cuando se desarrolla una jerarqua de clases en que algn
comportamiento est presente en todas ellas pero se materializa de forma
distinta para cada una. Por ejemplo, pensemos en una estructura de clases para
manipular figuras geomtricas. Podramos pensar en tener una clase genrica,
que podra llamarse FiguraGeometrica y una serie de clases que extienden a la
anterior que podran ser Circulo, Poligono, etc. Podra haber un mtodo dibujar
dado que sobre todas las figuras puede llevarse a cabo esta accin, pero las
operaciones concretas para llevarla a cabo dependen del tipo de figura en
concreto (de su clase). Por otra parte la accin dibujar no tiene sentido para la
clase genrica Figura Geometrica, porque esta clase representa una
abstraccin del conjunto de figuras posibles.
Para resolver esta problemtica Java proporciona las clases y mtodos
abstractos. Un mtodo abstracto es un mtodo declarado en una clase para el
cual esa clase no proporciona la implementacin (el cdigo). Una clase abstracta
es una clase que tiene al menos un mtodo abstracto. Una clase que extiende a
una clase abstracta debe implementar los mtodos abstractos (escribir el
cdigo) o bien volverlos a declarar como abstractos, con lo que ella misma se
convierte tambin en clase abstracta
Las clases abstractas en java
Una de las caractersticas ms tiles de cualquier lenguaje orientado a objetos es la
posibilidad de declarar clases que definen como se utiliza solamente, sin tener que implementar
mtodo. Esto en Java se hace mediante interfaces y con clases abstractas.
Una clase abstracta es una clase de la que no se puede crear objetos. La utilidad de estas
clases estriba en que otras clases hereden de sta, por lo que con ello conseguiremos reutilizar
cdigo. Para declarar una clase como abstracta utilizamos la palabra clave abstract.
Los mtodos para los que no aporte una implementacin sern declarados a su vz abstractos. Si
una clase tiene un mtodo abstract es obligatorio que la clase sea abstract. Todas las
subclases que hereden de una clase abstracta tendrn que redefinir los mtodos abstractos
dndoles una implementacin. En el caso de que no implementen alguno de esos mtodos la clase
hija tambin ser abstracta y tendr que declararse como tal (tanto la clase como los mtodos
que siguen siendo abstractos).
En mtodo abstract no pude ser static, ya que estos no pueden ser redifinidos por las
subclases.

4.3 Definicin, implementacin y herencia de interfaces

INTERFACES
Introduccin
Las interfaces Java son expresiones puras de diseo. Se trata de autnticas
conceptualizaciones no implementadas que sirven de gua para definir un
determinado concepto (clase) y lo que debe hacer, pero sin desarrollar un
mecanismo de solucin.
Se trata de declarar mtodos abstractos y constantes que posteriormente
puedan ser implementados de diferentes maneras segn las necesidades de un
programa.
Por ejemplo una misma interfaz podra ser implementada en una versin de
prueba de manera poco ptima, y ser acelerada convenientemente en la versin
definitiva tras conocer ms a fondo el problema.
Declaracin
Para declarar una interfaz se utiliza la sentencia interface, de la misma manera
que se usa la sentencia class:
interface MiInterfaz {
int CONSTANTE = 100;
int metodoAbstracto( int parametro );
}
Se observa en la declaracin que las variables adoptan la declaracin en
maysculas, pues en realidad actuarn como constantes final. En ningn caso
estas variables actuarn como variables de instancia.
Por su parte, los mtodos tras su declaracin presentan un punto y coma, en
lugar de su cuerpo entre llaves. Son mtodos abstractos, por tanto, mtodos
sin implementacin
Implementacin de una interfaz
Como ya se ha visto, las interfaces carecen de funcionalidad por no estar
implementados sus mtodos, por lo que se necesita algn mecanismo para dar
cuerpo a sus mtodos.
La palabra reservada implements utilizada en la declaracin de una clase indica
que la clase implementa la interfaz, es decir, que asume las constantes de la
interfaz, y codifica sus mtodos:

class ImplementaInterfaz implements MiInterfaz{


int multiplicando=CONSTANTE;
int metodoAbstracto( int parametro ){
return ( parametro * multiplicando );
}
}
En este ejemplo se observa que han de codificarse todos los mtodos que
determina la interfaz (metodoAbstracto()), y la validez de las constantes
(CONSTANTE) que define la interfaz durante toda la declaracin de la clase.
Una interfaz no puede implementar otra interfaz, aunque s extenderla
(extends) amplindola.
Herencia mltiple
Java es un lenguaje que incorpora herencia simple de implementacin pero que
puede aportar herencia mltiple de interfaz. Esto posibilita la herencia
mltiple en el diseo de los programas Java.
Una interfaz puede heredar de ms de una interfaz antecesora.
interface InterfazMultiple extends Interfaz1,Interfaz2{ }
Una clase no puede tener ms que una clase antecesora, pero puede
implementar ms de una interfaz:
class MiClase extends SuPadre implements Interfaz1,Interfaz2{ }
El ejemplo tpico de herencia mltiple es el que se presenta con la herencia en
diamante:
Imagen 6: Ejemplo de herencia mltiple
Para poder llevar a cabo un esquema como el anterior en Java es necesario que
las clases A, B y C de la figura sean interfaces, y que la clase D sea una clase
(que recibe la herencia mltiple):
interface A{ }
interface B extends A{ }
interface C extends A{ }
class D implements B,C{ }
Colisiones en la herencia mltiple
En una herencia mltiple, los identificadores de algunos mtodos o atributos
pueden coincidir en la clase que hereda, si dos de las interfaces padres tienen
algn mtodo o atributo que coincida en nombre. A esto se le llama colisin.

Esto se dar cuando las clases padre (en el ejemplo anterior B y C) tienen un
atributo o mtodo que se llame igual. Java resuelve el problema estableciendo
una serie de reglas.
Para la colisin de nombres de atributos, se obliga a especificar a qu interfaz
base pertenecen al utilizarlos.
Para la colisin de nombres en mtodos:

Si tienen el mismo nombre y diferentes parmetros: se produce


sobrecarga de mtodos permitiendo que existan varias maneras de llamar al
mismo.
Si slo cambia el valor devuelto: se da un error de compilacin,
indicando que no se pueden implementar los dos.

Si coinciden en su declaracin: se elimina uno de los dos, con lo que slo


queda uno.

Envolturas de los tipos simples


Los tipos de datos de Java no forman parte de la jerarqua de objetos. Sin
embargo a veces es necesario crear una representacin como objeto de alguno
de los tipos de datos simples de Java.
La API de Java contiene un conjunto de interfaces especiales para modificar el
comportamiento de los tipos de datos simple. A estas interfaces se las conoce
comoenvolturas de tipo simple.
Todas ellas son hijas de la clase abstracta Number y son:

Double: Da soporte al tipo double.

Float: Da soporte al tipo float.

Integer: Da soporte a los tipos int, short y byte.

Long: Da soporte al tipo long.

Character: Envoltura del tipo char.

Boolean: Envoltorio al tipo boolean.

Los mtodos abstractos son tiles cuando se quiere que cada implementacin
de la clase parezca y funcione igual, pero necesita que se cree una nueva clase
para utilizar los mtodos abstractos. Los interfaces proporcionan un
mecanismo para abstraer los mtodos a un nivel superior, lo que permite
simular la herencia mltiple de otros lenguajes.
Un interfaz sublima el concepto de clase abstracta hasta su grado ms alto. Un
interfaz podr verse simplemente como una forma, es como un molde,
solamente permite declarar nombres de mtodos, listas de argumentos, tipos
de retorno y adicionalmente miembros datos (los cuales podrn ser nicamente

tipos bsicos y sern tomados como constantes en tiempo de compilacin, es


decir, static y final).
Un interfaz contiene una coleccin de mtodos que se implementan en otro
lugar. Los mtodos de una clase son public, static y final.
La principal diferencia entre interface y abstract es que un interfaz
proporciona un mecanismo de encapsulacin de los protocolos de los mtodos
sin forzar al usuario a utilizar la herencia. Por ejemplo:
public interface VideoClip {
// comienza la reproduccion del video
void play();
// reproduce el clip en un bucle
void bucle();
// detiene la reproduccion
void stop();
}
Las clases que quieran utilizar el interfaz VideoClip utilizarn la palabra
implements y proporcionarn el cdigo necesario para implementar los mtodos
que se han definido para el interfaz:
class MiClase implements VideoClip {
void play() {
<cdigo>
}
void bucle() {
<cdigo>
}
void stop() {
<cdigo>
}
Al utilizar implements para el interface es como si se hiciese una accin
decopiar-y-pegar del cdigo del interface, con lo cual no se hereda nada,
solamente se pueden usar los mtodos.
La ventaja principal del uso de interfaces es que una clase interface puede ser
implementada por cualquier nmero de clases, permitiendo a cada clase
compartir el interfaz de programacin sin tener que ser consciente de la
implementacin que hagan las otras clases que implementen el interface.
class MiOtraClase implements VideoClip {
void play() {
<cdigo nuevo>

}
void bucle() {
<cdigo nuevo>
}
void stop() {
<cdigo nuevo>
}
Es decir, el aspecto ms importante del uso de interfaces es que mltiples
objetos de clases diferentes pueden ser tratados como si fuesen de un mismo
tipo comn, donde este tipo viene indicado por el nombre del interfaz.
Aunque se puede considerar el nombre del interfaz como un tipo de prototipo
de referencia a objetos, no se pueden instanciar objetos en s del tipo
interfaz. La definicin de un interfaz no tiene constructor, por lo que no es
posible invocar el operador new sobre un tipo interfaz.
Un interfaz puede heredar de varios interfaces sin ningn problema. Sin
embargo, una clase solamente puede heredar de una clase base, pero puede
implementar varios interfaces. Tambin, el JDK ofrece la posibilidad de definir
un interfaz vaco, como es el caso de Serialize, que permite serializar un
objeto. Un interfaz vaco se puede utilizar como un flag, un marcador para
marcar a una clase con una propiedad determinada.
La aplicacin java514.java, ilustra algunos de los conceptos referentes a los
interfaces. Se definen dos interfaces, en uno de ellos se definen dos
constantes y en el otro se declara un mtodo put() y un mtodo get(). Las
constantes y los mtodos se podran haber colocado en la misma definicin del
interfaz, pero se han separado para mostrar que una clase simple puede
implementar dos o ms interfaces utilizando el separador coma (,) en la lista de
interfaces.
Tambin se definen dos clases, implementando cada una de ellas los dos
interfaces. Esto significa que cada clase define el mtodo put() y el
mtodoget(), declarados en un interfaz y hace uso de las constantes definidas
en el otro interfaz. Estas clase se encuentran en ficheros separados por
exigencias del compilador, los ficheros son Constantes.java y MiInterfaz.java,
y el contenido de ambos ficheros es el que se muestra a continuacin:
public interface Constantes {
public final double pi = 6.14;
public final int constanteInt = 125;
}
public interface MiInterfaz {

void put( int dato );


int get();
}
Es importante observar que en la definicin de los dos mtodos del interfaz,
cada clase los define de la forma ms adecuada para esa clase, sin tener en
cuenta cmo estar definidos en las otras clases.
Una de las clases tambin define el mtodo show(), que no est declarado en el
interfaz. Este mtodo se utiliza para demostrar que un mtodo que no est
declarado en el interfaz no puede ser accedido utilizando una variable
referencia de tipo interfaz.
El mtodo main() en la clase principal ejecuta una serie de instanciaciones,
invocaciones de mtodos y asignaciones destinadas a mostrar las
caractersticas de los interfaces descritos anteriormente. Si se ejecuta la
aplicacin, las sentencias que se van imprimiendo en pantalla son
autoexplicactivas de lo que est sucediendo en el corazn de la aplicacin.
Los interfaces son tiles para recoger las similitudes entre clase no
relacionadas, forzando una relacin entre ellas. Tambin para declarar
mtodos que forzosamente una o ms clases han de implementar. Y tambin,
para tener acceso a un objeto, para permitir el uso de un objeto sin revelar su
clase, son los llamados objetos annimos, que son muy tiles cuando se vende un
paquete de clases a otros desarrolladores.

4.4 Definicin, uso y aplicaciones de las variables


polimrficas
Variables polimrficas
En Java, las variables que contienen objetos son variables polimrficas. El
trmino polimrfico (literalmente: muchas formas) se refiere al hecho de
que una misma variable puede contener objetos de diferentes tipos (del tipo
declarado o de cualquier subtipo del tipo declarado). El polimorfismo aparece
en los lenguajes orientados a objetos en numerosos contextos, las variables
polimrficas constituyen justamente un primer ejemplo.
Observemos la manera en que el uso de una variable polimrfica nos ayuda a
simplificar nuestro mtodo listar. El cuerpo de este mtodo es
for (Elemento elemento : elementos)
elemento.imprimir();

En este mtodo recorremos la lista de elementos (contenida en un ArrayList


mediante la variable elementos), tomamos cada elemento de la lista y luego
invocamos su mtodo imprimir. Observe que los elementos que tomamos de la
lista son de tipo CD o DVD pero no son de tipo Elemento. Sin embargo, podemos
asignarlos a la variable elemento (declarada de tipo Elemento) porque son
variables polimrficas. La variable elemento es capaz de contener tanto
objetos CD como objetos DVD porque estos son subtipos de Elemento.
Enmascaramiento de tipos (Casting)
Algunas veces, la regla de que no puede asignarse un supertipo a un subtipo es
ms restrictiva de lo necesario. Si sabemos que la variable de un cierto
supertipo contiene un objeto de un subtipo, podra realmente permitirse la
asignacin. Por ejemplo:
Vehiculo v;
Coche a = new Coche();
v = a; // Sin problemas
a = v; // Error, segn el compilador
Obtendremos un error de compilacin en a = v.
El compilador no acepta esta asignacin porque como a (Coche) tiene mas
atributos que v (Vehculo) partes del objeto a quedan sin asignacin. El
compilador no sabe que v ha sido anteriormente asignado por un coche.
Podemos resolver este problema diciendo explcitamente al sistema, que la
variable v contiene un objeto Coche, y lo hacemos utilizando el operador de
enmascaramiento de tipos, en una operacin tambin conocida como casting.
a = (Coche)v; // correcto
En tiempo de ejecucin, el Java verificar si realmente v es un Coche. Si
fuimos cuidadosos, todo estar bien; si el objeto almacenado en v es de otro
tipo, el sistema indicar un error en tiempo de ejecucin
(denominado ClassCastException) y el programa se detendr.
El compilador no detecta (Naturalmente) errores de enmascaramiento en
tiempo de compilacin. Se detectan en ejecucin y esto no es bueno.
El enmascaramiento debiera evitarse siempre que sea posible, porque puede
llevar a errores en tiempo de ejecucin y esto es algo que claramente no
queremos. El compilador no puede ayudamos a asegurar la correccin de este
caso.
En la prctica, raramente se necesita del enmascaramiento en un programa
orientado a objetos bien estructurado. En la mayora de los casos, cuando se

use un enmascaramiento en el cdigo, debiera reestructurarse el cdigo para


evitar el enmascaramiento, y se terminar con un programa mejor diseado.
Generalmente, se resuelve el problema de la presencia de un enmascaramiento
reemplazndolo por unmtodo polimrfico (Un poquito de paciencia).
La clase Object
Todas las clases tienen una superclase. Hasta ahora, nos puede haber
parecido que la mayora de las clases con que hemos trabajado no tienen una
superclase, excepto clases como DVD y CD que extienden otra clase. En
realidad, mientras que podemos declarar una superclase explcita para una
clase dada, todas las clases que no tienen una declaracin explcita de
superclase derivan implcitamente de una clase de nombre Object.
Object es una clase de la biblioteca estndar de Java que sirve como
superclase para todos los objetos. Es la nica clase de Java sin superclase.
Escribir una declaracin de clase como la siguiente
public class Person{} es equivalente a public class Person extends Object{}
Tener una superclase sirve a dos propsitos.
. Podemos declarar variables polimrficas de
tipo Object que pueden contener cualquier objeto
(esto no es importante)
. Podemos usar Polimorfismo (Ya lo vemos) y esto si es importante.
Autoboxing y clases envoltorio
Hemos visto que, con una parametrizacin adecuada, las colecciones pueden
almacenar objetos de cualquier tipo; queda un problema, Java tiene algunos
tipos que no son objetos.
Como sabemos, los tipos primitivos tales como int, boolean y char estn
separados de los tipos objeto. Sus valores no son instancias de clases y no
derivan de la clase Object. Debido a esto, no son suptipos de Object y
normalmente, no es posible ubicarlos dentro de una coleccin.
Este es un inconveniente pues existen situaciones en las que quisiramos crear,
por ejemplo, una lista de enteros (int) o un conjunto de caracteres (char).
Qu hacer?
La solucin de Java para este problema son las clases envoltorio. En Java, cada
tipo simple o primitivo tiene su correspondiente clase envoltorio que

representa el mismo tipo pero que, en realidad, es un tipo objeto. Por ejemplo,
la clase envoltorio para el tipo simple int es la clase de nombre Integer.
La siguiente sentencia envuelve explcitamente el valor de la variable ix de
tipoprimitivo int, en un objeto Integer:
Integer ienvuelto = new Integer(ix);
y ahora ienvuelto puede almacenarse fcilmente por ejemplo, en una coleccin
de tipo
ArrayList<Integer>. Sin embargo, el almacenamiento de valores primitivos en
un objeto coleccin se lleva a cabo an ms fcilmente mediante una
caracterstica del compilador conocida como autoboxing.
En cualquier lugar en el que se use un valor de un tipo primitivo en un contexto
que requiere un tipo objeto, el compilador automticamente envuelve al valor
de tipo primitivo en un objeto con el envoltorio adecuado. Esto quiere decir
que los valores de tipos primitivos se pueden agregar directamente a una
coleccin:
private ArrayList<Integer> listaDeMarcas;
public void almacenarMarcaEnLista (int marca){
listaDeMarcas.agregar(marca);
}
La operacin inversa, unboxing, tambin se lleva a cabo automticamente, de
modo que el acceso a un elemento de una coleccin podra ser:
int primerMarca = listaDeMarcas.remove(0);
El proceso de autoboxing se aplica en cualquier lugar en el que se pase como
parmetro un tipo primitivo a un mtodo que espera un tipo envoltorio, y
cuando un valor primitivo se almacena en una variable de su correspondiente
tipo envoltorio.
De manera similar, el proceso de unboxing se aplica cuando un valor de tipo
envoltorio se pasa como parmetro a un mtodo que espera un valor de tipo
primitivo, y cuando se almacena en una variable de tipo primitivo.
Tipo esttico y tipo dinmico
Volvemos sobre un problema inconcluso: el mtodo imprimir de DoME, no
muestra todos los datos de los elementos.

El intento de resolver el problema de desarrollar un mtodo imprimir completo


y polimrfico nos conduce a la discusin sobre tipos estticos y tipos
dinmicos y sobre despacho de mtodos. Comencemos desde el principio.
Necesitamos ver ms de cerca los tipos. Consideremos la siguiente sentencia:
Elemento e1 = new CD();
Cul es el tipo de e1?
Depende de qu queremos decir con tipo de e1.
El tipo de la variable e1 es Elemento; (tipo esttico)
El tipo del objeto almacenado en e1 es CD. (tipo dinmico)
Entonces el tipo esttico de e1 es Elemento y su tipo dinmico es CD.
En el momento de la llamada e1.imprimir(); el tipo esttico de la variable
elemento
es Elemento mientras que su tipo dinmico puede ser tanto CD como DVD. No
sabemos cul es su tipo ya que asumimos que hemos ingresado tanto objetos
CD como objetos DVD en nuestra base de datos.
Y en que clase debe estar codificado el mtodo imprimir()?
En tiempo de compilacin necesitamos de la existencia de imprimir() en la clase
Elemento, el compilador trabaja con tipo esttico.
En tiempo de ejecucin necesitamos de la existencia de un mtodo imprimir()
adecuado a los datos del objeto CD o DVD.
En definitiva, necesitamos de imprimir() en las tres clases. Aunque no ser lo
mismo lo que se imprima en cada uno de ellos. Lo que debemos hacer entonces
esSobrescribir el mtodo
Veamos el mtodo imprimir en cada una de las clases.
public class Elemento{

public void imprimir(){


System.out.print(titulo + " (" + duracion + " minutos) " );
if (loTengo){System.out.println("*");
}
else {System.out.println();}
System.out.println(" " + comentario);
}
}
public class CD extends Elemento{
public void imprimir(){
System.out.println(" " + interprete);
System.out.println(" temas: " + numeroDeTemas);

}
}
public class DVD extends Elemento{
public void imprimir(){
System.out.println(" director: " + director);
}
}
Este diseo funciona mejor: compila y puede ser ejecutado, aunque todava no
est perfecto. Proporcionamos una implementacin de este diseo mediante el
proyecto dome-v3.
La tcnica que usamos ac se denomina sobrescritura (algunas veces tambin
se hace referencia a esta tcnica como redefinicin). La sobrescritura es una
situacin en la que un mtodo est definido en una superclase (en este ejemplo,
el mtodo imprimir de la clase Elemento) y un mtodo, con exactamente la
misma signatura, est definido en la subclase.
En esta situacin, los objetos de la subclase tienen dos mtodos con el mismo
nombre y la misma signatura: uno heredado de la superclase y el otro propio de
la subclase.
Cul de estos dos se ejecutar cuando se invoque este mtodo?
Bsqueda dinmica del mtodo (Despacho dinmico)
Si ejecutamos el mtodo listar de la BaseDeDatos, podremos ver que se
ejecutarn los mtodos imprimir de CD y de DVD pero no el de Elemento, y
entonces la mayor parte de la informacin, la comn contenida en Elemento, no
se imprime.
Que est pasando? Vimos que el compilador insisti en que el
mtodo imprimir est en la clase Elemento, no le alcanzaba con que los
mtodos estuvieran en las subclases. Este experimento ahora nos muestra que
el mtodo de la clase Elemento no se ejecuta para nada, pero s se ejecutan los
mtodos de las subclases.
Ocurre que el control de tipos que realiza el compilador es sobre el tipo
esttico, pero en tiempo de ejecucin los mtodos que se ejecutan son los
que corresponden al tipo dinmico.
Saber esto es muy importante pero todava insuficiente.
Para comprenderla mejor, veamos con ms detalle cmo se invocan los mtodos.
Este procedimiento se conoce como bsqueda de mtodo, ligadura de mtodo o
despacho de mtodo. En este libro, nosotros usamos la terminologa bsqueda
de mtodo.

Comenzamos con un caso bien sencillo de bsqueda de mtodo. Suponga que


tenemos un objeto de clase DVD almacenado en una variable v1 declarada de
tipo DVD (Figura 9.5). La clase DVD tiene un mtodo imprimir y no tiene
declarada ninguna superclase.
Esta es una situacin muy simple que no involucra herencia ni polimorfismo.
Ejecutamos v1.imprimir{). Esto requiere de las siguientes acciones:
l. Se accede a la variable v1.
2. Se encuentra el objeto almacenado en esa variable (siguiendo la referencia).
3. Se encuentra la clase del objeto (siguiendo la referencia es instancia de).
4. Se encuentra la implementacin del mtodo imprimir en la clase y se ejecuta.
Hasta aqu, todo es muy simple.
A continuacin, vemos la bsqueda de un mtodo cuando hay herencia. El
escenario es similar al anterior, pero esta vez la clase DVD tiene una
superclase, Elemento, y el mtodo imprimir est definido slo en la superclase
Ejecutamos la misma sentencia. La invocacin al mtodo comienza de manera
similar: se ejecutan nuevamente los pasos 1 al 3 del escenario anterior pero
luego contina de manera diferente:
4. No se encuentra ningn mtodo imprimir en la clase DVD.
5. Se busca en la superclase un mtodo que coincida. Y esto se hace hasta
encontrarlo, subiendo en la jerarqua hasta Object si fuera necesario. Tenga
en cuenta que, en tiempo de ejecucin, debe encontrarse definitivamente un
mtodo que coincida, de lo contrario la clase no habra compilado.
6. En nuestro ejemplo, el mtodo imprimir es encontrado en la clase
Este ejemplo ilustra la manera en que los objetos heredan los mtodos.
Cualquier mtodo que se encuentre en la superclase puede ser invocado sobre
un objeto de la subclase y ser correctamente encontrado y ejecutado.
Ahora llegamos al escenario ms interesante: la bsqueda de mtodos con una
variable polimrfica y un mtodo sobrescrito. Los cambios:
. El tipo declarado de la variable v1 ahora es Elemento, no DVD.
. El mtodo imprimir est definido en la clase Elemento y redefinido (o
sobrescrito) en la clase DVD.
Este escenario es el ms importante para comprender el comportamiento de
nuestra aplicacin DoME y para encontrar una solucin a nuestro problema con
el mtodo imprimir.
Los pasos que se siguen para la ejecucin del mtodo son exactamente los
mismos pasos 1 al 4, primer caso

Observaciones:
. No se usa ninguna regla especial para la bsqueda del mtodo en los casos en
los que el tipo dinmico no sea igual al tipo esttico.
. El mtodo que se encuentra primero y que se ejecuta est determinado por el
tipo dinmico, no por el tipo esttico. La instancia con la que estamos
trabajando es de la clase DVD, y esto es todo lo que cuenta.
Los mtodos sobrescritos en las subclases tienen precedencia sobre los
mtodos de las superclases. La bsqueda de mtodo comienza en la clase
dinmica de la instancia, esta redefinicin del mtodo es la que se encuentra
primero y la que se ejecuta.
Esto explica el comportamiento que observamos en nuestro proyecto DoME.
Los mtodos imprimir de las subclases (CD y DVD) slo se ejecutan cuando se
imprimen los elementos, produciendo listados incompletos. Como podemos
solucionarlo?
Llamada a super en mtodos
Ahora que conocemos detalladamente cmo se ejecutan los mtodos
sobrescritos podemos comprender la solucin al problema de la impresin. Es
fcil ver que lo que queremos lograr es que, para cada llamada al mtodo
imprimir de, digamos un objeto CD, se ejecuten para el mismo objeto tanto el
mtodo imprimir de la clase Elemento como el de la clase CD. De esta
manera se imprimirn todos los detalles.
Cuando invoquemos al mtodo imprimir sobre un objeto CD, inicialmente se
invocar al mtodo imprimir de la clase CD. En su primera sentencia, este
mtodo se convertir en una invocacin al mtodo imprimir de la superclase que
imprime la informacin general del elemento. Cuando el control regrese del
mtodo de la superclase, las restantes sentencias del mtodo de la subclase
imprimirn los campos distintivos de la clase CD.
public void imprimir(){ // Mtodo imprimir de la clase CD
super.imprimir();
System.out.println(" " + interprete);
System.out.println(" temas: ") + numeroDeTemas);
}
Detalles sobre diferencias del super usado en constructores:
El nombre del mtodo de la superclase est explcitamente establecido. Una
llamada a super en un mtodo siempre tiene la forma super.nombre-delmtodo(parmetros);

La llamada a super en los mtodos puede ocurrir en cualquier lugar dentro de


dicho mtodo. No tiene por qu ser su primer sentencia.
La llamada a super no se genera, es completamente opcional.
Mtodo polimrfico
Lo que hemos discutido en las secciones anteriores, desde Tipo esttico y tipo
dinmico hasta ahora, es lo que se conoce como despacho de mtodo
polimrfico (o mas simplemente, Polimorfismo).
Recuerde que una variable polimrfica es aquella que puede almacenar objetos
de diversos tipos (cada variable objeto en lava es potencialmente polimrfica).
De manera similar, las llamadas a mtodos en lava son polimrficas dado que
ellas pueden invocar diferentes mtodos en diferentes momentos. Por ejemplo,
la sentenciaelemento.imprimir(); puede invocar al mtodo imprimir de CD en un
momento dado y al mtodo imprimir de DVD en otro momento, dependiendo del
tipo dinmico de la variable elemento.
Bueno, no hay mucho ms por ver en herencia y polimorfismo. Claro que para
consolidar esto necesitamos verlo funcionando.
Para hacer mas completo el demo de polimorfismo, vamos a incorporar un
elemento ms:
Libro, que extiende directamente Elemento, sin incorporarle ningn atributo
adicional.
import java.util.ArrayList;
public class BaseDeDatos{
private ArrayList<Elemento> elementos;
protected String auxStr;
public BaseDeDatos(){ // constructor
elementos = new ArrayList<Elemento>();
}
public void agregarElemento (Elemento elElemento){
elementos.add(elElemento);
}
public String toString(){ // Cadena con todos los elementos contenidos
auxStr = "Contenidos BaseDeDatos\n";
auxStr+=elementos.toString();
return auxStr;
}
}
package dome;

public class Elemento{


private String titulo;
private int duracion;
private boolean loTengo;
private String comentario;
public Elemento(String elTitulo, int tiempo){
titulo = elTitulo;
duracion = tiempo;
loTengo = false;
comentario = "";
}
public String toString(){
String aux = titulo + " (" + duracion + " minutos) ";
if (loTengo)aux += "*";
aux += " " + comentario+"\n";
return aux;
}
}
package dome;
public class CD extends Elemento{
private String interprete;
private int numeroDeTemas;
public CD(String elTitulo, String elInterprete, int temas, int tiempo){
super(elTitulo, tiempo);
interprete = elInterprete;
numeroDeTemas = temas;
}
public String toString(){
String aux = super.toString();
aux+= " interprete (CD): " + interprete+"\n";
aux+= " temas: " + numeroDeTemas+"\n";
return aux;
}
}
package dome;
public class DVD extends Elemento{
private String director;
public DVD(String elTitulo, String elDirector, int time){

super(elTitulo, time);
director = elDirector;
}
public String toString(){
String aux = super.toString();
aux+= " director (DVD): " + director+"\n";
return aux;
}
}
package dome;
public class Libro extends Elemento{
public Libro(String elTitulo, int time){
super(elTitulo, time);
}
}
package dome;
// @author
public class Main {
private BaseDeDatos db;
public void DemoBaseDedatos(){
System.out.println("Demo inicia");
db = new BaseDeDatos();
Elemento elem;
// Incluyo 2 CDs
elem = new CD("Pajaros en la Cabeza","Amaral",14,35);
db.agregarElemento(elem);
elem = new CD("One chance","Paul Pots",10,30);
db.agregarElemento(elem);
// Incluyo 2 DVDs
elem = new DVD("Soy Leyenda","Francis Lawrence",120);
db.agregarElemento(elem);
elem = new DVD("Nada es Para Siempre","Robert Redford",105);
db.agregarElemento(elem);
// Incluyo dos libros
elem = new Libro("El Seor de los Anillos",5000);
db.agregarElemento(elem);
elem = new Libro("El Don Apacible",10000);
db.agregarElemento(elem);

// veamos que hemos hecho


System.out.println(db.toString());
System.out.println("Demo terminado");
}
public static void main(String[] args) {
Main demo = new Main();
demo.DemoBaseDedatos();
}
}
La
sentencia System.out.println(db.toString()), mtodo
public
voidDemoBaseDedatos() es la que se ejecuta inicialmente. Esta sentencia:
Incorpora
en
la
cadena
el
resultado
de elementos.toString. Como elementos es una instancia de ArrayList, usa el
toString() de esta clase (De ah los corchetes de cierre y las comas
separadoras).
- elementos contiene 6 instancias de la variable polimrfica Elemento:
- las dos primeras tienen tipo dinmico CD. Entonces, en la ejecucin del
toString() propio invocan super.toString() (el de Elemento) y luego completan
con los datos especficos de CD.
- Las dos siguientes tienen tipo dinmico DVD. Proceden exactamente lo mismo
que CD.
- Las dos ltimas instancias tienen tipo dinmico Libro. Como no tienen
toString() propio, el despacho dinmico encuentra el de Elemnto y este es el
que se ejecuta.
Complicado o facil? En todo caso, la programacin es muy sinttica, nada de
sobreescritura,
cada
parte
del
armado
de
la
cadena
que
imprimeSystem.out.println(db.toString()) lo hace el mtodo del objeto
responsable de ello, como manda la POO.

4.5 Reutilizacin del cdigo


Lo primero que se les viene a la cabeza a los estudiantes (y a muchos
profesionales) cuando se les menciona la reutilizacin del cdigo es el famoso
copiar y pegar al que se han acostumbrado en la programacin estructurada, y
de hecho muchos lo hacen en poo, lo cual es una de las practicas que ms
encarece el desarrollo de software. Como todo en Java, el problema se
resuelve con las clases. Para reutilizar el cdigo creamos nuevas clases pero, en
lugar de partir de cero, partimos de clases, relacionadas con nuestra clase, que

han sido ya creadas y depuradas. El truco est en usar las clases sin ensuciar el
cdigo
existente.
Una forma de hacer esto es crear objetos de nuestras clases existentes
dentro de la nueva clase. Esto se conoce como composicin porque la nueva
clase est compuesta de objetos de clases existentes. Estamos reutilizando la
funcionalidad
del
cdigo,
y
no
la
forma.
Otra forma es crear una nueva clase como un tipo de una clase ya existente.
Tomamos la forma de la clase existente y aadimos cdigo a la nueva, sin
modificar la clase existente. Esta forma de crear nuevos objetos se llamada
herencia, y lo que hacemos es extender la clase en la que nos basamos para
crear la nueva.
Composicin:
Hasta ahora hemos usado la composicin de cierta manera, ej. Cuando hacemos
una interfaz grfica de usuario, nuestra clase de interfaz grfica esta
compuesta por un frame, unos panel, botones, etc. todos estos objetos
componen el objeto de interfaz grfica. Es decir que la composicin consiste en
poner manejadores de objetos dentro de nuestra clase, estos manejadores de
objetos no sern otra cosa que instancias de las clases en las que nos estamos
basando para crear la nueva clase.
Recordemos que la forma para determinar cundo usar composicin es cuando
podemos decir que nuestra nueva clase tiene un elemento de otro tipo de
objetos, por ejemplo un cronmetro tiene: horas, minutos y segundos, es decir
que una clase Cronometro est compuesta por otras clases llamadas: Horas,
Minutos y Segundos.
Veamos como seria esta clase:
public class Cronometro {
Horas h;
Minutos m;
Segundos s;
String cadena;
int seg,min,hor;
public Cronometro() {
seg=0;

min=0;
hor=0;
h = new Horas();
m = new Minutos();
s = new Segundos();
cadena = new String("0 : 0 : 0");
}
public String avanzar(){
seg = s.forward();
if(seg==0){
min=m.forward();
if(min==0){
hor=h.forward();
}
}
cadena = hor + " : " + min + " : " + seg;
return cadena;
}
public String reset(){
seg = s.reset();
min = m.reset();
hor = h.reset();
cadena = hor + " : " + min + " : " + seg;
return cadena;
}
}
Nuestra clase Cronometro est compuesta entre otras cosas por objetos del
tipo Horas, Minutos y Segundos y a travs del constructor de nuestra clase
creamos las instancias de cada una de ellas.
Herencia:
En java aunque no establezcamos de manera explcita la herencia siempre est

presente, ya que todas las clases que creemos heredan de la clase Object, por
eso es vlido decir que en java todo es un objeto. La sintaxis para la
composicin es obvia pero, para realizar la herencia, hay una forma claramente
distinta. Cuando heredamos, estamos diciendo "Esta nueva clase es como esa
clase antigua", por ejemplo es decir que la clase Horas es una Unidad De
Tiempo. Afirmamos esto en el cdigo dando el nombre de la clase como siempre
pero, antes de la apertura del lmite cuerpo de clase, pondremos la palabra
clave "extends" seguida por el nombre de la clase base. Cuando hagamos esto,
obtendremos automticamente todos los datos miembros y mtodos de la clase
base.
Primero veamos como seria la clase UnidadDeTiempo:
public class UnidadDeTiempo {
int valor;
int tope;
public int forward(){
if(valor == tope)
valor=0;
else
valor++;
return valor;
}
public int reset(){
valor=0;
return valor;
}
}
Y nuestra clase Horas:
public class Horas extends Unidad De Tiempo{
public Horas() {
this.valor=0;
this.tope=23;

}
}
De esta manera sin necesidad de tener que escribir nuevamente todos el
cdigo de Unidad De Tiempo lo tememos disponible en la clase Horas, pero que
partes tenemos disponibles?, todos los atributos y los mtodos de la clase
padre estn disponibles en la clase hija pero dependiendo de los modificadores
de acceso o visibilidad de estos, por ejemplo los atributos y mtodos de tipo
friendly solo estarn disponibles para las clases hijas que heredan de una clase
padre en el mismo paquete, los atributos y mtodos de tipo public estarn
disponibles para todas las clases que hereden de la clase padre sin importar
que se halle o no en el mismo paquete; los miembros protected tambin son
accesibles desde las clases hijas.
El cdigo de nuestra clases hijas no tienen porque limitarse solo al cdigo
heredado, de hecho casi siempre la herencia se hace para extender la
funcionalidad de las clases heredadas aadiendo nuevos mtodos y atributos.
La composicin y la herencia:
Tanto la composicin como la herencia permiten poner sub-objetos dentro de
tu nueva clase. Podramos preguntarnos cul es la diferencia entre los dos, y
cundo elegir uno en lugar del otro. La composicin es generalmente usada
cuando deseamos las caractersticas de una clase existente dentro de una
nueva clase, pero no su interfaz. Es decir, ponemos un para poder usarlo para
implementar caractersticas de nuestra nueva clase, pero el usuario de esa
nueva clase ver el interfaz que hemos definido en lugar del interfaz del
objeto
insertado.
Los objetos miembros usan la implementacin ocultndose a s mismos, por lo
que esto es una cosa segura a hacer y, cuando el usuario sabe que estamos
uniendo un conjunto de partes, hace que el interfaz sea ms fcil de entender.
Cuando heredamos, estamos cogiendo una clase existente y creando una
versin especial de esa clase. En general, esto significa que estamos tomando
una clase de propsito general, especializndola para un caso o necesidad
particular. Pensando un poco, podr entender que no tendra sentido construir
un coche usando un objeto vehculo (un coche no contiene un vehculo, es un
vehculo!). La relacin es- un viene expresada por la herencia, y la relacin tiene
un viene expresada por la composicin.
}

Potrebbero piacerti anche