POLIMORFISMO EN JAVA, ENLAZADO DINÁMICO
El polimorfismo es una de las características fundamentales
de la Programación Orientada a Objetos y está estrechamente relacionado con la
herencia.
Una jerarquía de clases, los métodos y clases abstractas, la
sobrescritura de métodos y las conversiones entre clases de la jerarquía
sientan las bases para el polimorfismo. Es necesario entender bien estos
conceptos para comprender el polimorfismo. En esta entrada se explican y se ven
algunos ejemplos de herencia.
El polimorfismo se puede
definir como la cualidad que tienen los objetos para responder de distinto modo
a un mismo mensaje.
Para conseguir un comportamiento polimórfico en
un programa Java se debe cumplir lo siguiente:
- Los métodos deben estar declarados
(métodos abstractos) y a veces también pueden estar implementados (métodos no
abstractos) en la clase base.
- Los métodos debes estar redefinidos en
las clases derivadas.
- Los objetos deben ser manipulados
utilizando referencias a la clase base.
Ejemplo de polimorfismo en Java:
Vamos
a ver más claro todo lo anterior con un ejemplo. Tenemos la siguiente clase
abstracta Polígono:
//Clase abstracta Poligono
public abstract class Poligono {
private int numLados;
public Poligono() {
}
public Poligono(int numLados) {
this.numLados = numLados;
}
public int getNumLados() {
return numLados;
}
public void setNumLados(int numLados) {
this.numLados = numLados;
}
//Sobreescritura del método toString() heredado de Object
@Override
public String toString(){
return "Numero de lados: " + numLados;
}
//Declaración del método abstracto area()
public abstract double area();
}
Esta clase tiene un atributo entero numLados. Además contiene el método
abstracto area() y se ha modificado (Override) el método toString() heredado de
Object.
A partir de la clase Poligono vamos a crear las
clases Rectangulo y Triangulo como derivadas de ella.
//Clase Rectangulo hereda de Poligono
public class Rectangulo extends Poligono{
private double lado1;
private double lado2;
public Rectangulo() {
}
public Rectangulo(double lado1, double lado2) {
super(2);
this.lado1 = lado1;
this.lado2 = lado2;
}
public double getLado1() {
return lado1;
}
public void setLado1(double lado1) {
this.lado1 = lado1;
}
public double getLado2() {
return lado2;
}
public void setLado2(double lado2) {
this.lado2 = lado2;
}
// Sobreescritura del método toString() heredado de Poligono
@Override
public String toString() {
return "Rectangulo " + super.toString() +
"\nlado 1 = " + lado1 + ", lado 2 = " + lado2;
}
//Implementación del método abstracto area() heredado de Poligono
@Override
public double area(){
return lado1 * lado2;
}
}
//Clase Triangulo hereda de Poligono
public class Triangulo extends Poligono{
private double lado1;
private double lado2;
private double lado3;
public Triangulo() {
}
public Triangulo(double lado1, double lado2, double lado3) {
super(3);
this.lado1 = lado1;
this.lado2 = lado2;
this.lado3 = lado3;
}
public double getLado1() {
return lado1;
}
public void setLado1(double lado1) {
this.lado1 = lado1;
}
public double getLado2() {
return lado2;
}
public void setLado2(double lado2) {
this.lado2 = lado2;
}
public double getLado3() {
return lado3;
}
public void setLado3(double lado3) {
this.lado3 = lado3;
}
// Sobreescritura del método toString() heredado de Poligono
@Override
public String toString() {
return "Triangulo " + super.toString() +
"\nlado 1 = " + lado1 + ", lado 2 = " + lado2 + ", lado 3 = " + lado3;
}
//Implementación del método abstracto area() heredado de Poligono
@Override
public double area(){
double p = (lado1+lado2+lado3)/2;
return Math.sqrt(p * (p-lado1) * (p-lado2) * (p-lado3));
}
}
Vamos
a escribir un programa para utilizar estas clases y ver como funciona el
polimorfismo. Vamos a crear objetos de tipo Triángulo y Rectángulo y los
guardaremos en un ArrayList. En lugar de tener dos arrays distintos uno para
triángulos y otro para rectángulos, los guardaremos todos juntos utilizando un
ArrayList de Poligonos.
El
programa creará objetos, leerá sus datos por teclado y los guardará en el
ArrayList de Polígonos. A continuación se recorrerá el array y se mostrarán los
datos de cada objeto y su área.
//Clase Principal
public class Polimorfismo1 {
static Scanner sc = new Scanner(System.in);
// ArrayList de referencias a objetos de la clase base Poligono
static ArrayList<Poligono> poligonos = new ArrayList();
public static void main(String[] args) {
leerPoligonos();
mostrarPoligonos();
}
//Se pide por teclado el tipo de Poligono a leer y se ejecuta el método
//leer correspondiente
public static void leerPoligonos() {
int tipo;
do {
do {
System.out.print("Tipo de poligono 1-> Rectangulo 2-> Triangulo 0-> FIN >>> ");
tipo = sc.nextInt();
} while (tipo < 0 || tipo > 2);
if (tipo != 0) {
switch (tipo) {
case 1:
leerRectangulo();
break;
case 2:
leerTriangulo();
break;
}
}
} while (tipo != 0);
}
//Se crea un rectángulo y se añade al array
public static void leerRectangulo() {
double l1, l2;
System.out.println("Introduzca datos del Rectángulo");
do {
System.out.print("Longitud del lado 1: ");
l1 = sc.nextDouble();
} while (l1 <= 0);
do {
System.out.print("Longitud del lado 2: ");
l2 = sc.nextDouble();
} while (l2 <= 0);
Rectangulo r = new Rectangulo(l1, l2);
poligonos.add(r);
//En esta instrucción se produce una conversión implícita o upcasting
//Se asigna una referencia de una clase derivada (Rectangulo)
//a una referencia de la clase base (Poligono) ya que el ArrayList es
//de tipo Poligono
}
//Se crea un triángulo y se añade al array
Public static void leerTriangulo() {
double l1, l2, l3;
System.out.println("Introduzca datos del Triangulo");
do {
System.out.print("Longitud del lado 1: ");
l1 = sc.nextDouble();
} while (l1 <= 0);
do {
System.out.print("Longitud del lado 2: ");
l2 = sc.nextDouble();
} while (l2 <= 0);
do {
System.out.print("Longitud del lado 3: ");
l3 = sc.nextDouble();
} while (l3 <= 0);
Triangulo t = new Triangulo(l1, l2, l3);
poligonos.add(t);
//conversión implícita o upcasting igual que en el método anterior
}
public static void mostrarPoligonos() {
//Se recorre el ArrayList poligonos que contiene
//referencias a Triangulos y Rectangulos.
//A p de tipo Poligono se le asignarán mediante upcasting referencias a objetos
//de tipo Triangulo o Rectangulo
for(Poligono p: poligonos){
System.out.print(p.toString());
System.out.printf(" area: %.2f %n", p.area());
}
}
}
Después
de ejecutar el método leerPoligonos, el ArrayList poligonos contiene mezclados
triángulos y rectángulos. Por ejemplo, podría contener lo siguiente:
triangulo
|
triangulo
|
rectangulo
|
triangulo
|
rectangulo
|
rectangulo
|
triangulo
|
…
|
Mediante el bucle for
for(Poligono p: poligonos)
se
recorre el array y se asigna a la variabale p de tipo Poligono (Clase base)
cada elemento del array. En la instrucción p.toString() mediante p se invoca al
método toString(). Pero, ¿a qué método toString se ejecutará? Se ejecutará el
método toString de las clase derivada a la que pertenece el objeto referenciado
por la variable p (Triangulo o Rectangulo)
Lo
mismo ocurre cuando se invoca al método area(). Se ejecutará el método area de
la clase derivada a la que pertenece el objeto referenciado por p.
En este ejemplo se produce el polimorfismo ya
que:
-Los métodos toString() y area() están declarados
en la clase base. El método toString está además implementado en la clase base.
-Estos métodos están redefinidos en las clases
derivadas.
-Se invocan mediante referencias a la clase base
Poligono.
El polimorfismo es posible porque, como ya hemos
visto en la entrada correspondiente a la Herencia, cuando se invoca un método
mediante una referencia a una clase base, se ejecuta la versión del método
correspondiente a la clase del objeto referenciado y no al de la clase de la
variable que lo referencia.
Por lo
tanto, el método que se ejecuta se decide durante la ejecución del programa.
A este
proceso de decidir en tiempo de ejecución qué método se ejecuta se le denomina enlazado dinámico.
El enlazado
dinámico es el mecanismo que hace posible el polimorfismo.
El
enlazado dinámico es lo opuesto a la habitual
vinculación estática o enlazado estático que consiste en decidir en tiempo de
compilación qué método se ejecuta en cada caso.
En el programa anterior podemos ver donde se produce enlazado dinámico y
donde enlazado estático:
public void mostrarPoligonos() {
for(Poligono p: poligonos){
//Para que el polimorfismo se lleve a cabo se está
//aplicando el enlazado dinámico.
//Durante la ejecución se decide qué método se ejecuta.
System.out.print(p.toString());
System.out.printf(" area: %.2f %n", p.area());
}
}
public static void main(String[] args) {
// Enlazado estático. Cuando se compila el programa
//ya se sabe que serán esos métodos los que se van a ejecutar.
leerPoligonos();
mostrarPoligonos();
}
Excelente!!!
ResponderEliminarinolvidable asi me dicen mis ex´s amores
ResponderEliminarEste comentario ha sido eliminado por el autor.
ResponderEliminargenial
ResponderEliminarbuena explicación
ResponderEliminarGracias, espero que te haya servido.
Eliminar