jueves, 23 de octubre de 2008

Introducción


En el presente trabajo hablaremos del polimorfismo, que es uno de los fundamentos para cualquier lenguaje orientado a Objetos. Esta palabra que significa "múltiples formas", permite a una interface ser usada por una clase general de acciones. La acción concreta a llevar a cabo se determina por la naturaleza específica de la situación.

En ocasiones, cuando se lleva a cabo el procesamiento del polimorfismo es necesario programar “en forma específica”.
Con el polimorfismo es posible diseñar e implementar sistemas que puedan extenderse fácilmente. Pueden agregarse nuevas clases con pocas modificaciones (o si acaso ninguna) a las porciones genéricas del programa, siempre y cuando esa clases formen parte de la jerarquía de herencia que el programa procese en forma genérica. Las únicas partes de un programa que deben alterarse para dar cabida a nuevas clases son aquellos componentes del programa que requiera de un conocimiento directo de las nuevas clases que el programador agregar a la jerarquía.

En términos más generales, el concepto de polimorfismo a menudo se expresa por la frase "una interfaz, múltiples métodos".

Esto significa que es posible diseñar una interfaz genérica para un grupo de actividades relacionadas. Es evidente que esta forma de trabajar ayuda a reducir la complejidad del diseño, pues permite usar una misma interfaz para especificar un conjunto de acciones similares. Será el compilador el que tendrá que seleccionar la acción concreta (esto es, el método) para aplicar en cada situación.

Como estudiantes de Ingeniería de Sistemas, nosotros tenemos que conocer la interfaz general. En el siguiente apartado se relata casos prácticos que ayudará a entender los beneficios del polimorfismo.

En programación orientada a objetos se denomina polimorfismo a la capacidad que tienen objetos de diferentes clases de responder al mismo mensaje o evento.
Esto significa que puede haber muchos mensajes con el mismo nombre, en diferentes clases. Cada Clase responde al mensaje con su código propio (o método).
También se puede aplicar a la propiedad que poseen algunas operaciones de tener un comportamiento diferente dependiendo del objeto (o tipo de dato) sobre el que se aplican.
El polimorfismo sólo es aplicable si cualquiera de los posibles tipos de objetos que se invoquen tienen definida la operación empleada, y los tipos de datos de entrada requeridos y los valores devueltos, más allá de cómo se empleen o calculen, son compatibles entre sí.

1. Definición



En programación orientada a objetos se denomina polimorfismo a la capacidad que tienen los objetos de una clase de responder al mismo mensaje o evento en función de los parámetros utilizados durante su invocación. Un objeto polimórfico es una entidad que puede contener valores de diferentes tipos durante la ejecución del programa.
El concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. Así nacen los conceptos de funciones polimórficas y tipos polimórficos. Las primeras son aquellas funciones que pueden evaluarse o ser aplicadas a diferentes tipos de datos de forma indistinta; los tipos polimórficos, por su parte, son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no está especificado.
El concepto de Polimorfismo es uno de los fundamentos para cualquier lenguaje orientado a Objetos, las mismas raíces de la palabra pueden ser una fuerte pista de su significado: Poli = Múltiple, morfismo= Formas, esto implica que un mismo Objeto puede tomar diversas formas.

En Java, el polimorfismo de métodos se implementa con la sobrecarga de métodos: métodos con el mismo nombre, y mismo número y tipo de argumentos.

Una variable también puede ser polimórfica, es decir, cambiar de forma o de clase de objeto al que referencia durante la ejecución de un programa. El polimorfismo de una variable en Java está limitado de tal forma que una variable que referencia a un objeto sólo puede especializarse, tal y como ocurre en la vida real.

Con el polimorfismo es posible diseñar e implementar sistemas que puedan extenderse más fácilmente. Pueden sobrescribirse programas para procesar objetos de tipos que tal vez cuando el programador se encuentre en desarrollo.



El concepto de Polimorfismo es uno de los fundamentos para cualquier lenguaje orientado a Objetos, las mismas raíces de la palabra pueden ser una fuerte pista de su significado: Poli = Multiple, morfismo= Formas, esto implica que un mismo Objeto puede tomar diversas formas.
A través del concepto de Herencias ("Inheritance") es posible ilustrar este comportamiento:
El poder manipular un Objeto como si éste fuera de un tipo genérico otorga mayor flexibilidad al momento de programar con Objetos, el término Polimorfismo también es asociado con un concepto llamado Late-Binding (Ligamiento Tardío), observe el siguiente fragmento de código:
Figura a = new Circulo(); Figura b = new Triangulo();
Inicialmente se puede pensar que este código generaría un error debido a que el tipo de referencia es distinta a la instancia del objeto, sin embargo, el fragmento anterior es correcto y demuestra el concepto de Polimorfismo; para asentar este tema se describe un ejemplo más completo:
Uso de Polimorfismo

El uso de Polimorfismo posee ciertos detalles que no fueron descritos en el ejemplo anterior, uno de estos, que también tiene implicaciones en otros componentes es: Casting.
Uso de "Casting"
El término "Casting" viene de la palabra "Cast" que significa Molde, por lo que el termino literal es Hacer un Molde, en Polimorfismo se lleva a cabo este proceso de "Casting" implícitamente, una Guitarra se coloca en el molde de un Instrumento, un Triangulo en el molde de una Figura, sin embargo, en ciertas ocasiones se requiere realizar un tipo de "Casting" que no es considerado seguro en términos computacionales.
Anteriormente se mencionó que el "Casting" llevado a cabo con Polimorfismo es implícito, esto se debe a que no se requiere de sintaxis especial, simplemente se convierte una Guitarra a un Instrumento, sin embargo, para llevar una transformación en sentido opuesto se requiere de sintaxis adicional para mantener la seguridad de transformación; analicemos: mientras se puede asegurar que un Triangulo es una Figura ("Up-Casting"), una Figura no necesariamente es un Triangulo, claro está que lo puede ser, pero en Java se requiere definir explícitamente esta operación ("Down-Casting").
El uso práctico de "Down-Casting" será explorado en la sección de Vectores y "Hashtables".

2. Clasificación




Se puede clasificar el polimorfismo en dos grandes clases:
2.1 Polimorfismo dinámico (o polimorfismo paramétrico) es aquél en el que el código no incluye ningún tipo de especificación sobre el tipo de datos sobre el que se trabaja. Así, puede ser utilizado a todo tipo de datos compatible.
Conceptos iníciales:
Un identificador o variable es simplemente un nombre
Un valor describe los contenidos actuales de la memoria asociados con una variable
Existe situaciones en las que el tipo (clase) de la variable no concuerdan con el tipo (clase) que
Contiene el valor. La clase de la variable se conoce como clase estática y la clase del valor se conoce como la clase dinámica

Visión inicial de polimorfismo
Hemos dicho que tenemos:
Tipo estático de una variable: es el tipo que posee una determinada referencia deducible del texto del programa y conocido en tiempo de compilación.
Tipo dinámico de una referencia: es el tipo de esa referencia en tiempo de ejecución.
Cuando hay polimorfismo ocurre que solo hay un tipo estático y varios dinámicos.

Ejemplo:
Object a;
a = new Integer (5);
/* Tipo estático: Object
Tipo dinámico: Integer */
a = new Double (5.0);
/* Tipo estático: Object
Tipo dinámico: Double */

Ejemplo (continuación): Es muy importante que exista una relación de herencia entre los tipos estáticos y dinámicos, de forma que el estático sea una superclase de los dinámicos.



Ejemplo:
Double elemento;
elemento = new Double (5.0);
elemento = new Integer (5);
/* ¡¡¡Falla porque un Integer no es un
Double!!! */

El polimorfismo dinámico me permite tener varios códigos para una misma signatura y que el código que se ejecute al final depende del contexto en el que me encuentre.
2.2 Polimorfismo estático (o polimorfismo ad hoc) es aquél en el que los tipos a los que se aplica el polimorfismo deben ser explicitados y declarados uno por uno antes de poder ser utilizados.
El polimorfismo dinámico unido a la herencia es lo que en ocasiones se conoce como programación genérica.
También se clasifica en herencia por redefinición de métodos abstractos y por método sobrecargado. El segundo hace referencia al mismo método con diferentes parámetros.
Otra clasificación agrupa los polimorfismo en dos tipos: Ad-Hoc que incluye a su vez sobrecarga de operadores y coerción, Universal (inclusión o controlado por la herencia, paramétrico o genericidad). En el momento de emplear estas operaciones, el lenguaje es capaz de ejecutar el código
Correspondiente dependiendo de la clase del objeto sobre el que se ejecuta la operación. Esto permite definir un interfaz común, un aspecto externo idéntico, para una serie de clases.

3. Conversión de objetos

El polimorfismo visto previamente está basado en utilizar referencias de un tipo más “amplio” que los objetos que los apuntan. Las ventajas del polimorfismo son evidente, pero hay una importante limitación: el tipo de la referencia (clase abstracta, clase base o interfase) limita los métodos que se pueden utilizar y las variables miembros a las que se pueden acceder. Por ejemplo, un objeto puede tener una referencia cuyo tipo sea una interfase, aunque sólo en el caso en que su clase o una de sus super-clases implementen dicha interfase. Un objeto cuya referencia es un tipo de interfase sólo puede utilizar los métodos definidos en dicha interfase. Dicho de otro modo, ese objeto no puede utilizar las variables y los métodos propios de su clase. De esta forma la referencia de tipo interfase define, limitan y unifican la forma de utilizarse de objetos pertenecientes a clases muy distintas (que implementan dicha interfase).
Si se desea utilizar todos los métodos y acceder a todas las variables que las clases de un objeto permite, hay que utilizar un cast explícito, que convierta su referencia más general que en la tipo específico del objeto.
De aquí una parte importante del interés del cast entre objetos (más bien entre referencias, habría que decir).
Para la conversión entre objetos de distintas clases, java exige que dichas clases estén relacionadas por herencia (una deberá ser subclase de la otra). Se realiza una conversión implícita o automática de una subclase a una superclase siempre que se necesite, ya que el objeto de la subclase siempre tiene toda la información necesaria para ser utilizado en lugar de un objeto de la superclase. No importa que la superclase no sea capaza de contener toda la información de la subclase.
La conversión en sentido contrario -utilizar un objeto de una superclase donde se espera encontrar uno de la subclase- debe hacerse de modo explícito y puede producir errores por falta de información o de métodos. Si falta información, se obtiene una ClassCastException.
No puede acceder a las variables exclusivas de la subclase a través de una referencia de la superclase. Sólo se pueden utilizar los métodos definidos en la superclase, aunque la definición utilizada para dichos métodos sea la de la subclase
Por ejemplo, supóngase que se crea un objeto de una subclase B y se referencia con un nombre de una superclase A,
A a=new B ();
En este caso el objeto creado dispone de más información de la que la referencia a le permite acceder (podría ser, por ejemplo, una nueva variable miembro j declarada en B). Para acceder a esta información adicional hay que hacer un cast explícito en la forma (B)a. Para imprimir esa variable j habría que escribir (los paréntesis son necesarios):
System.out.println (((B) a).j);
Un cast de un objeto a la superclase puede permitir utilizar variables –no métodos- de la superclase, aunque estén redefinidos en la subclase. Considérese el siguiente ejemplo: la clase C deriva de B y B deriva de A. las tres definen una variable x. En este caso, si desde el código de la subclase C se utiliza:

X //se accede a la x de C
this.x //se accede a la x de C
super.x //se accede a la x de B. sólo se puede subir un nivel
((B) this).x //se accede a la x de B
((A) this).x //se accede a la x de A

4. Polimorfismo de sobrecarga

4.1 SOBRECARGA:

Varias implementaciones del mismo método con distintos parámetros (cambio de interfaz). Muy habitual en constructores.
› Es aquí donde realmente se aprecia los beneficios del polimorfismo.
› Sobrecargando el constructor conseguimos dotar a la clase de flexibilidad.
› Java permite que varios métodos dentro de una clase se llamen igual, siempre y cuando su lista de parámetros sea distinta. Por ejemplo así, se podría definir la siguiente clase:

Dos funciones, procedimientos u operadores que se llaman igual se distinguen por el tipo de sus argumentos o por el número de sus argumentos.

"SumaGenerica" que aglutinara las sumas de todos los tipos primitivos:
class SumaGenerica {
int suma (int a, int b) {
return a+b; }
double suma (double a, double b) {
return a+b; } }

En este caso se dice que el método está sobrecargado y el proceso de definir un método así se conoce como sobrecarga del método. La sobrecarga de métodos es una de las maneras en que Java implementa el polimorfismo.



4.2 SOBREESCRITURAS DE MÉTODOS:

Ø Una subclase puede modificar los métodos que ha heredado del padre.
Una subclase puede crear un método con diferente funcionalidad al método del padre, pero con el mismo:
l Nombre
l Tipo de retorno
l Lista de argumentos

Ø Llamada de métodos virtuales.
Empleado e = new Jefe;
e.getDetails ( );

Se comprueba el tipo de la referencia estática (Empleado) en tiempo de compilación y el tipo de la referencia dinámica (Jefe) en tiempo de ejecución.
¿Cual de los getDetails () se ejecutará, el de la clase Empleado o el de la clase Jefe?
Se obtiene el comportamiento del método asociado a la clase de la variable y no el comportamiento asociado al tipo de la variable en el compilador. Por tanto se ejecuta e.getDetails () ejecutando el del tipo real del objeto, es decir el método de Jefe. A este comportamiento se le suele llamar invocación de métodos virtuales. Hay una diferencia muy importante entre C++ y Java. En C++, sólo se modifica el comportamiento si se define el método como virtual. En los lenguajes OO puros, esto es normal, pero con ello C++ gana tiempo en ejecución.

Para poder sobreescribir métodos en las clases descendientes se debe verificar:
l El tipo de retorno de los dos métodos ha de ser igual.
l El método de la subclase no puede ser menos accesible que el de la clase padre.
l El método de la subclase no puede provocar más excepciones que el método del padre.

Sobreescritura de métodos:

public class Padre {
public void método (){ }
}
public class Hijo extends Padre {
public void método (){ }
\\no puede ser private
}
public class OtraClase{
public void otroMetodo () {
Padre p1 = new Padre ();
Padre p2 = new Hijo ();
p1.método();
p2.método(); }}


EJEMPLO:

int a=5;
int b=6;
int c=a+b; // c vale 11
String a=“Ho”;
String b=“la”;
String c=a+b; // c vale “Hola”


EJEMPLO:

class Gato {
public Gato (){…}
public Gato (String nombre){…}
public void comer (Planta planta){…} // método 1
public void comer (Planta planta, int x, int y){…} //2
public void comer (ComidaGatos comida){…} //3
}

Ejemplo
class Gato {
public Gato () {…}
public Gato (String nombre){…}
public void comer (Planta planta){…} // método 1
public void comer (Planta planta, int x, int y){…} //2
public void comer (Comida Gatos comida){…} //3
}
Gato gato = new Gato (“Ruffy2);
Planta planta = new Planta ();
ComidaGatos comida = new ComidaGatos();
gato.comer (planta); //ejecuta método 1
gato.comer (planta, 3,4); //ejecuta método 2
gato.comer (comida); // ejecuta el método 3

Class Gato {
public Gato (){…}
public Gato (String nombre){…}
public void comer (Planta planta) {…} // metodo 1
public void comer (Geranio planta) {…} // método 2
public void comer (Roedor rt) {…} // método 3
public void comer (Object o) {…} // método 4
}
class Geranio extends Planta{…};
Gato gato=new Gato();;
Geranio geranio= new Geranio ();
gato. comer (geranio);
/* ¿Método 1, 2 o 4? “Geranio” es una Planta
y un “Geranio” y un “Object”. En teoría todos
los métodos son factibles ¿por cual se
decanta? */

Ejemplos

EJEMPLOS DE POLIMORFISMO:

EJEMPLO 1:
import java.util.*;
class Instrumento {
public void tocar () {
System.out.println ("Instrumento.tocar()");
}
public String tipo () {
return "Instrumento";
}
public void afinar () {}
}
class Guitarra extends Instrumento {
public void tocar() {
System.out.println ("Guitarra.tocar()");
}
public String tipo () {return "Guitarra";}
public void afinar () {}
}
class Piano extends Instrumento {
public void tocar () {
System.out.println ("Piano.tocar()");
}
public String tipo () {return "Piano";}
public void afinar () {}
}
class Saxofon extends Instrumento {
public void tocar () {
System.out.println("Saxofon.tocar()");
}
public String tipo () {return "Saxofon";}
public void afinar () {}
}
// Un tipo de Guitarra
class Guzla extends Guitarra {
public void tocar() {
System.out.println ("Guzla.tocar()");
}
public void afinar () {
System.out.println ("Guzla.afinar ()");
}
}
// Un tipo de Guitarra
class Ukelele extends Guitarra {
public void tocar () {
System.out.println ("Ukelele.tocar()");
}
public String tipo () {return "Ukelele";}
}
public class Musica {
// No importa el tipo de Instrumento,
// Seguirá funcionando debido a Polimorfismo:
Static void afinar (Instrumento i) {
// ...
i.tocar ();
}
Static void afinarTodo (Instrumento [] e) {
for (int i = 0; i < orquesta =" new" i =" 0;">Título: Polimorfimo


*

Descripción:


*

Copyright: Copyright (c) 2007


*

Empresa:


* @author : Grecia, Lía, Oscar, Valeria
* @version 1.0
*/

public class SumaGenericaApp {
public static void main (String[] args) {

int v;int w;
float x; float y;

JTextArea Arsuma = new JTextArea();

SumaGenerica s1;
SumaGenerica s2;

s1 = new SumaGenerica();
s2= new SumaGenerica();

Arsuma.setBackground(Color.PINK);

Arsuma.append("\n****Operaciones con Polimorfismo****\n");
Arsuma.append("\n*****Suma de Numeros Enteros*****\n*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n");
v = Integer.parseInt(JOptionPane.showInputDialog("Ingrese el primer numero Entero "));
w= Integer.parseInt(JOptionPane.showInputDialog("Ingrese el Segundo Numero Entero"));
s1.suma(v,w);
Arsuma.append("\n La suma es:\t"+s1.suma(v,w));

Arsuma.append("\n\n*****Suma de Numeros Reales*****\n*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*\n");
x = Float.parseFloat(JOptionPane.showInputDialog("Ingrese el primer numero Decimal "));
y= Float.parseFloat(JOptionPane.showInputDialog("Ingrese el Segundo Numero Decimal"));
s2.suma(x,y);
Arsuma.append("\t\n La suma es: \t"+s2.suma(x,y));
JOptionPane.showMessageDialog(null,Arsuma,"Suma",JOptionPane.PLAIN_MESSAGE);
s1= null;
s2= null;
System.exit(0);