Está en la página 1de 2

Tema 11

Polimorfismo (Objetivo 5.2)

5.2 Dado un escenario, desarrollar un codigo que demuestre el uso del polimorfismo. Mas adelante, determina cuando es necesario
un casting y reconocer los errores del compilador en tiempo de ejecucion en los objetos que hacen referencia a ese casting.

Recuerda, que cualquier objeto en Java que pase mas de un test de tipo ES-UN se le considera polimorfico. Aparte de los objetos de
tipo Object, todos los objetos son polimorficos si pasan el test de ES-UN para su propio tipo y para la clase Object.
Recuerda que la unica manera de acceder a un objeto es a traves de sus variables de referencia y existen unas pocas claves para
recordar sobre referencias:

Una variable de referencia puede ser solo de un tipo, y una vez declarada, ese tipo nunca puede ser cambiado (aunque la
referencia al objeto cambie).
Una referencia es una variable, asi que le pueden reasignar a otros objetos (a menos que sean declaradas como final).
El tipo de la variable de referencia determina los metodos que pueden ser invocados en el objeto al que la variable hace
referencia.
Una variable de referencia puede ser referida a cualquier objeto del mismo tipo declarado en la referencia, o - esto es lo grande
- puede ser referido a cualquier subtipo del tipo declarado!
Una variable de referencia puede ser declarada como un tipo clase o un tipo interfaz. Si la variable es declarada como un tipo
interfaz, puede referenciar cualquier objeto de cualquier clase que implemente la interfaz.

Antes habiamos creado una clase GameShame que era heredada por otras dos clases, PlayerPiece y TitlePiece. Ahora imagina que
quieres animar algunas de las figuras (shapes) del tablero de juego. Pero no todas las figuras pueden ser animadas, asi que, que hacer
con la herencia de la clase?
Podriamos crear una clase con un metodo animate() y asi tener solo algunas de las subclases heredadas de GameShape? Si podemos,
entonces podriamos tener PlayerPiece por ejemplo, herandando a GameShape y Animatable, mientras TitlePiece solo heredaria
GameShape. Pero esto no funciona! Java solo soporta la herencia simple! Esto quiere decir que una clase solo tiene una superclase.
En otras palabras, si PlayerPiece es una clase, no podemos hacer esto:

1 class PlayerPiece extends GameShape, Animatable { // NO! ?

2 // mas codigo
3 }

Una clase solo puede heredar una clase. Esto quiere decir un padre por clase. Una clase puede tener multiples hijos, asi que la clase B
podria heredar de la clase A, y la clase C podria heredar de la B, y asi sucesivamente. Las clases pueden tener multiples arbol de
herencia, pero no es lo mismo decir que una clase hereda directamente dos (o mas) clases.

En algunos lenguajes como C++, se permite que una clase herede mas de una clase. Esta capacidad se la conoce como herencia
multiple. La razon de que los creadores de Java no permiten la herencia multiple es que el codigo puede quedar confuso. El problema
es que si una clase hereda dos clases, y ambas superclases tienen el metodo doStuff(), que version de doStuff() seria la que estamos
heredando? Este problema puede conducir a un escenario conocido como "Diamantes letales", porque la figura de la clase diagrama
spuede ser creada en un diseño de herencias multiples. El diamante se forma cuando las clases B y C heredan a A, y ambas B y C
heredan un metodo de A. Si la clase D hereda B y C, y B y C tienen los mismos metodos sobrescritos de A, la clase D tiene en teoria,
dos implementaciones distintas del mismo metodo. Si se dibuja la clase en un diagrama, la figura de las cuatro clases pareceria un
diamante.

Asi que si eso no funciona, que mas podrias hacer? Simplemente, podrias poner animate() en el codigo de la clase GameShape, y
entonces, inhabilitar el metodo en las clases que no puedan ser animadas. Pero eso es una mala eleccion en cuanto a diseño por
muchas razones, incluyendo que hay mas propension a errores, y hace a GameShape menos cohesivo (hablaremos mas sobre
cohesion en un minuto), y esto quiere decir que GameShape pone a disponsicion el metodo de animar a todas las subclases, cuando
la realidad es que solo algunas de las subclases pueden usar este metodo.
Asi que, que mas podrias hacer? Ya sabes la respuesta, crear la interfaz Animatable, y que solo las subclases que puedan ser
animadas implementen esta interfaz. Aqui esta la interfaz:

1 public interface Animatable { ?

2 public void animate();


3 }

Y aqui esta la clase PlayerPuece modificada que implementa la interfaz:

1 class PlayerPiece extends GameShape implements Animatable { ?

2 public void movePiece() {


3 System.out.println("moviendo pieza del juego");
4 }

1
5
6 public void animate() {
7 System.out.println("animando...");
8 }
9 // mas codigo
10 }

Asi ahora tenemos una clase PlayerPiece que pasa el test ES-UN para la clase GameShape y la interfaz Animatable. Esto significa
que PlayerPiece puede ser tratada poliformicamente como una de las cuatro cosas que puede ser, dependiendo del tipo de la variable
de referencia:

Un objeto
Un GameShape (desde el mismo momento que PlayerPiece heredo GameShape)
Un PlayerPiece (es lo que realmente es)
Un Animatable (desde el momento en que PlayerPiece implementa Animatable)

La siguiente declaracion es legal, observala antentamente:

1 PlayerPiece player = new PlayerPiece(); ?

2 Object o = player;
3 GameShape shape = player;
4 Animatable mover = player;

Tan solo hay un objeto aqui, una instancia de tipo PlayerPiece, pero hay cuatro tipos de variables de referencia, todas se refieren a ese
objeto inicial. Cual de las variables de referencia puede invocar el metodo display()? Solo dos de las cuatro.
Recuerda que las invocaciones permitidas a los metodos por el compilador se basan solamente en el tipo de referencia declarada, sin
importar el tipo de objeto. Asi que mirando los cuatro tipos de referencia otra vez, Object, GameShape, PlayerPiece y Animatable,
cual de estos cuatro objetos dispone del metodo display()?

Por supuesto, las clases GameShape y PlayerPiece son las que el compilador sabe que contienen em metodo display(), asi que son
estos los que pueden invocar a ese metodo. Recuerda que para el compilador, un PlayerPiece ES-UN GameShape, asi que el
compilador dice "Veo que el tipo declarado es PlayerPiece, y en el momento que PlayerPiece hereda GameShape, quiere decir que
PlayerPiece hereda el metodo display(). Asi que, PlayerPiece puede usar el metodo display()".
Que metodos pueden ser invocados cuando PlayerPiece esta siendo referido desde una variable declarada como Animatable? Solo el
metodo animate().
Naturalmente, lo bueno es que cualquier clase del arbol de herencias puede tambien implementar Animatable, lo que quiere decir que
si tienes un metodo con un argumento declarado como Animatable, puedes pasarle PlayerPiece, SpinningLogo, y cualquier cosa que
implemente Animatable.
Y puedes usar el parametro (de tipo Animatable) para invocar el metodo animate(), pero no el display() (el cual podria no tenerlo), o
cualquier cosa que el compilador conozca basado en el tipo de la referencia. El compilador siempre sabe los metodos que puedes
invocar en cualquier objeto, asi que estos estan seguros de ser llamados sin importar la referencia, clase o interface usadas para
referir al objeto.
Hemos dejado una gran parte de todo esto, se trata de que incluso el compilador solo sabe sobre los tipos de referencias declarados,
la JVM en tiempo de ejecucion sabe lo que es el objeto realmente. Esto quiere decir que si el metodo display() del objeto PlayerPiece
se invoca desde una variable de referencia de tipo GameShpae, si PlayerPiece sobrescribe el metodo display(), la JVM invocara la
version de PlayerPiece!
La JVM usa el metodo sobrescrito ya que es asi como lo heredo la subclase (obvio, no?). No te olvides nunca de esto:

Las invocaciones de un metodo polimorfico se aplican solo a los metodos de instancia. No puedes siempre refererirte a un
objeto con mas de una variable de referencia (una superclase o interface), pero en tiempo de ejecucion, las unicas cosas que
son seleccionadas dinamicamente basadas en el objeto actual (mas que en el tipo de referencia) son los metodos de la
instancia.
No son los metodos estaticos, ni las variables. Solo la sobrescritura de los metodos de instancia se invocan dinamicamente en
el tipo real del objeto.

Para entender esta definicion hace falta el entendimiento de la sobrescritura y la distincion entre metodos estaticos y metodos de
instancia, que sera lo proximo que veremos.

Tema 10, orientado a objetos kimeraweb.com.es Tema 12, casting de variables de referencia

También podría gustarte