Poliformismo

  video[Tutorial] El Polimorfismo en Java

La clase Object

En las clases que hemos creado al principio de este módulo (Complejo y GeoPunto) no se utilizaba la palabra extends para heredar de otra clase. Sin embargo, en estos casos Java crea la nueva clase heredando de la clase Object. La clase Object es la raíz del árbol genealógico de herencias en Java. Esta clase dispone de una serie de métodos que por tanto van a estar disponibles en todos los objetos que creemos. Entre estos métodos se incluye: toString(), getClass()o equals(Object o).

Un objeto de la clase hija también lo es de la clase padre y de todos sus antecesores. Por ejemplo, un objeto de la clase ComplejoAmpliado también lo será de la clase Complejo y también de  Object . De hecho todo objeto en Java será necesariamente de la clase Object.

Esta característica que acabamos de comentar es una propiedad de la herencia, en este apartado vamos a estudiar otro aspecto importante de la programación orientada a objetos, el polimorfismo.

Polimorfismo

El polimorfismo consiste en declarar un objeto de una clase, pero instanciarlo como un descendiente de dicha clase (lo contrario no es posible). Es decir, utilizaremos la siguiente expresión:

<Clase_padre> <objeto> = new <Clase_hija>(<parámetros>);

A primera vista no parece demasiado útil, pero en la práctica va a tener una gran potencia. Para explicarlo utilizaremos un ejemplo real. Cuando estemos desarrollando una aplicación Android y queremos introducir un botón en el interfaz de usuario, vamos a poder indicar un gráfico de fondo. Para indicar este fondo tendremos que indicar un objeto de la clase  Drawable . La clase Drawable representa algo que se puede dibujar, tiene muchos descendientes: BitmapDrawable (un fichero PNG o JPG), ShapeDrawable (un gráfico a partir de primitivas vectoriales),  GradienDrawable  (degradado de color), AnimationDrawable (animaciones frame a frame), … La clase Drawable no permite declarar un objeto directamente de esta clase. Un Drawable es algo que se puede dibujar pero que no sabemos cómo. Un objeto real ha de declararse de algunos de sus descendientes, que si saben cómo dibujarse. Esto da mucha potencia a la hora de escoger el tipo de fondo para nuestro botón, podemos usar un fichero PNG, un degradado, una animación,... Y más todavía, podríamos crear nuestra propia clase, AnimacionLucesNavidad, que descenda de AnimationDrawable. Un objeto de esta clase también lo sería de Drawable y por lo tanto vamos a poder utilizarlo como fondo de nuestro botón.

Si reflexionamos sobre lo que hemos conseguido gracias al polimorfismo veremos que no es poca cosa. Los diseñadores de la clase botón (realmente se llama Button) pudieron tomar una decisión de diseño, concretamente cómo podríamos dibujar el fondo del botón, sin la necesidad de especificar exactamente cómo hacerlo. Más adelante hemos podido diseñar nuevas clases para dibujar este fondo simplemente creando descendientes de la clase Drawable.

Conversión de tipos

Imaginemos que hemos declarado un objeto de una clase pero lo hemos construido utilizando un constructor de una clase descendiente, tal y como se muestra a continuación:

Complejo c =  new  ComplejoAmpliado(12.4);

Vamos a poder utilizar cualquier método de c definido en la clase Complejo o cualquiera de sus descendientes:

c.toString();

c.getClass();

Pero la siguiente método no podrá ser utilizado:

c.esReal()..

Esto es debido a que esté método no está definido para la clase Complejo. No obstante, estamos seguros de que c es de la clase ComplejoAmpliado y por lo tanto este método sí que puede ser llamado. Para solucionar este problema, podemos utilizar lo que en Java se conoce como typecast  (cambio de tipo).

(<Clase>) <objeto>

A partir de este momento <objeto> será considerado de la clase <Clase>. Aunque no  haya sido declarada de esta clase. Por lo tanto podremos escribir:

((ComplejoAmpliado)c).esReal()

Uso de instanceof

Podemos averiguar si un objeto es de una determinada clase usando la siguiente expresión:

<objeto> instanceof <Clase>

A continuación se muestran algunos ejemplos:

Complejo c = new ComplejoAmpliado(12.4);

if (c instanceof Object)… //siempre true
if (c instanceof Complejo)… //true
if (c instanceof ComplejoAmpliado)… //true
if (((ComplejoAmpliado)c).esReal())…

Ejercicio paso a paso: Polimorfismo con la clase Complejo

1.    Abre la clase Principal del proyecto Complejos.

2.    Reemplaza el código del método main() por el siguiente:

Complejo lista[] = new Complejo[4];               
lista[0] = new Complejo(-1.5, 3.0);
lista[1] = new Complejo(-1.2, -3.0);
lista[2] = new ComplejoAmpliado(-1.5, 3.0);
lista[3] = new ComplejoAmpliado(-1.2, 0);
for (int i = 0; i < lista.length; i++) {
   System.out.println("Complejo: " + lista[i]);
}

Observa como en la primera línea definimos un array de 4 complejos. En las siguientes líneas inicializamos las 4 posiciones del array, las 2 primeras con objetos de la clase Complejo y las dos siguientes con objetos de la clase ComplejoAmpliado. Finalmente, realizamos un bucle para todos los elementos del array mostrando en la consola el valor de cada complejo.  

3.    Trata de resolver la siguiente pregunta: Recuerda que mostrar un complejo supone una llamada al método toString() que ha sido definido de manera diferente en la clase Complejo y en ComplejoAmpliado. Teniendo en cuenta que los elementos del array han sido declarados como Complejo, pero algunos han sido creados como Complejo y otros como ComplejoAmpliado, Cuando tenga que llamar a toString() ¿A cuál de los dos métodos se llamará?

4.    Para salir de dudas pulsa el botón Ejecución y observa el resultado:

Complejo: -1.5+3.0i
Complejo: -1.2+-3.0i
Complejo: -1.5+3.0i
Complejo: -1.2 ¡Real!

Uno podría pensar que siempre se va a llamar al método toString() definido en Complejo. Pero, el último resultado nos indica que no siempre es así, si no que depende de la clase usada en el constructor.

5.    Reemplaza el bucle for por el siguiente:

for(int i = 0; i < lista.length; i++) {
  System.out.println("Complejo: " + lista[i]);
  if(lista[i] instanceof ComplejoAmpliado){
    System.out.println("  esReal=" + ((ComplejoAmpliado) lista[i]).esReal());
  }
}

Observa como hemos añadido un if que verifica si el objeto es de la clase ComplejoAmpliado. En caso afirmativo se le aplica un typecast al objeto para considerarlo de esta clase y así poder llamar al método esReal(). El resultado es mostrado en la consola.

6.    Ejecuta el proyecto y observa el resultado:

Complejo: -1.5+3.0i
Complejo: -1.2+-3.0i
Complejo: -1.5+3.0i
  esReal=false
Complejo: -1.2 ¡Real!
  esReal=true

 

Practica: Polimorfismo con la clase Object

Crea un array de 4 objetos de la clase Object. Construye el primero como Complejo y el segundo como Geopunto y los dos últimos como GeoPuntoAlt. Implementa un bucle que recorra el array mostrando los objetos. En caso de tratarse de un punto geográfico con una altitud superior a 1000 m, muestra  un mensaje con el texto “punto elevado”.