Polimorfismo:
La palabra polimorfismo proviene del griego y significa que
posee varias formas diferentes. Este es uno de los conceptos esenciales
de una programación orientada a objetos. Así como la herencia está relacionada
con las clases y su jerarquía, el polimorfismo se relaciona con los métodos.
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.
En algunos lenguajes, el término polimorfismo es
también conocido como ‘Sobrecarga de parámetros’ ya que las características de
los objetos permiten aceptar distintos parámetros para un mismo método
(diferentes implementaciones) generalmente con comportamientos distintos e
independientes para cada una de ellas.
En programación orientada a objetos, el polimorfismo se refiere a la propiedad por la que
es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos. El único requisito que deben cumplir los
objetos que se utilizan de manera polimórfica es saber responder al mensaje que
se les envía.
La apariencia del código puede ser muy diferente
dependiendo del lenguaje que se utilice, más allá de las obvias diferencias
sintácticas.
Por ejemplo, en un lenguaje de programación que cuenta con
un sistema de tipos dinámico (en los que las variables pueden contener datos
de cualquier tipo u objetos de cualquier clase) como Smalltalk no se requiere que los objetos que se utilizan de modo
polimórfico sean parte de una jerarquía de clases
El
polimorfismo es una relajación del sistema de tipos, de tal manera que una
referencia a una clase (atributo, parámetro o declaración local o elemento de
un vector) acepta direcciones de objetos de dicha clase y de sus clases
derivadas (hijas, nietas,…).
En general, hay tres tipos de polimorfismo:
Polimorfismo de
sobrecarga
Polimorfismo paramétrico (también llamado polimorfismo de plantillas)
Polimorfismo de
inclusión (también llamado redefinición o subtipado)
Polimorfismo de sobrecarga
El
polimorfismo de sobrecarga ocurre cuando las funciones del mismo nombre
existen, con funcionalidad similar, en clases que son completamente
independientes una de otra (éstas no tienen que ser clases secundarias de la
clase objeto). Por ejemplo, la clase complex, la clase image y la clase link
pueden todas tener la función "display". Esto significa que no
necesitamos preocuparnos sobre el tipo de objeto con el que estamos trabajando
si todo lo que deseamos es verlo en la pantalla.
Por lo
tanto, el polimorfismo de sobrecarga nos permite definir operadores cuyos
comportamientos varían de acuerdo a los parámetros que se les aplican. Así es
posible, por ejemplo, agregar el operador + y hacer que se
comporte de manera distinta cuando está haciendo referencia a una operación
entre dos números enteros (suma) o bien cuando se encuentra entre dos cadenas
de caracteres (concatenación).
Polimorfismo paramétrico
El
polimorfismo paramétrico es la capacidad para definir varias funciones
utilizando el mismo nombre, pero usando parámetros diferentes (nombre y/o
tipo). El polimorfismo paramétrico
selecciona automáticamente el método correcto a aplicar en función del
tipo de datos pasados en el parámetro.
Por lo
tanto, podemos por ejemplo, definir varios métodos homónimos de addition() efectuando una suma
de valores.
- El método int addition(int,int) devolvería
la suma de dos números enteros.
- float addition(float, float) devolvería la suma de dos flotantes.
- char addition (char, char) daría por resultado la suma de dos caracteres definidos por
el autor.
- etc.
Una signature es el nombre y tipo
(estático) que se da a los argumentos de una función. Por esto, una firma de
método determina qué elemento se va a llamar.
Polimorfismo
de subtipado
La habilidad
para redefinir un método en clases que se hereda de una clase base se llama especialización. Por lo tanto, se
puede llamar un método de objeto sin tener que conocer su tipo intrínseco: esto
es polimorfismo de subtipado. Permite no tomar en cuenta detalles
de las clases especializadas de una familia de objetos, enmascarándolos con una
interfaz común (siendo esta la clase básica).
Imagine un
juego de ajedrez con los objetos rey, reina, alfil, caballo, torre y peón, cada uno heredan el
objeto pieza.
El método movimiento podría,
usando polimorfismo de subtipado, hacer el movimiento correspondiente de
acuerdo a la clase objeto que se llama. Esto permite al programa realizar el movimiento de pieza sin tener
que verse conectado con cada tipo de pieza en particular.
Clasificación Se puede clasificar el
polimorfismo en dos grandes clases:
•
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.
•
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).
Ejemplo:
‘este método devuelve la suma de 2 parámetros
Function suma (int numero1, int numero2)
‘este método devuelve la suma de los 3 parámetros
Function suma (int numero1, int numero2, int numero3)
‘este método devuelve la concatenación de cadena1 y cadena2
Function suma (String cadena1, String cadena2.
Ejemplo: polimorfismo.
public class
A extends B {
public void print() {
System.out.println("A");
}
}
public class
B {
public void print() {
System.out.println("B");
}
}
public class
C extends B {
public void print() {
System.out.println("C");
}
}
public class Main {
public static void
main(String[]
args) {
B[]
bs = new
B[3];
bs[0] = new
B();
bs[1] = new
A();
bs[2] = new
C();
naivePrinter(bs);
}
private static void
naivePrinter(B[]
bs) {
for (int
i = 0;
i < bs.length;
i++) {
bs[i].print();
}
}
}
Manejo de excepciones.
El manejo de excepciones es una técnica de programación que permite al programador controlar los errores ocasionados durante la ejecución de un programa informático. Cuando ocurre cierto tipo de error, el sistema
reacciona ejecutando un fragmento de código que resuelve la situación, por
ejemplo retornando un mensaje de error o devolviendo un valor por defecto.
Una excepción en términos de lenguaje
de programación es la indicación de un problema que ocurre durante la
ejecución de un programa. Sin embargo, la palabra excepción se refiere a que este
problema ocurre con poca frecuencia generalmente cuando existe algún dato o
instrucción que no se apega al funcionamiento del programa por lo que se
produce un error. El manejo de excepciones permite al usuario crear
aplicaciones tolerantes a fallas y robustos (resistentes a errores) para
controlar estas excepciones y que pueda seguir ejecutando el programa sin verse
afectado por el problema. En lenguaje java estas excepciones pueden manejarse
con las clases que extienden el paquete Throwable de manera directa o
indirecta, pero existen diversos tipos de excepciones y formas para manejarlas.
El manejo de excepciones ayuda al programador a trasladar
el código para manejo de errores de la línea principal de ejecución, además se
puede elegir entre manejar todas las excepciones, las de cierto tipo o de las
de grupos relacionados, esto hace que la probabilidad de pasar por alto los errores
se reduzca y a la vez hace los programas más robustos. Pero es importante
utilizar un lenguaje de programación que soporte este manejo, de lo contrario
el procesamiento de errores no estará incluido y hará el programa más
vulnerable. Este manejo está diseñado para procesar errores que ocurren cuando
se ejecuta una instrucción, algunos ejemplos son: desbordamiento aritmético,
división entre cero, parámetros inválidos de método y asignación fallida en la
memoria. Sin embargo, no está diseñado para procesar problemas con eventos
independientes al programa como son pulsar una tecla o clic al mouse.
Las excepciones se dividen en verificadas y no
verificadas. Es importante esta división porque el compilador implementa
requerimientos de atrapar o declarar para las verificadas lo que hará que se
detecten las excepciones automáticamente y de acuerdo al lenguaje de
programación utilizado se utilizará un método para corregirlas. Sin embargo
para las no verificadas se producirá un error indicando que deben atraparse y
declararse. Por eso el programador debe pensar en los problemas que pueden
ocurrir cuando se llama a un método y definir excepciones para verificarse
cuando sean importantes. Las clases de excepciones pueden derivarse de una
superclase común, por lo que con un manejador para atrapar objetos de la
superclase, también se pueden atrapar todos los objetos de las subclases de esa
clase. Pero también, se pueden atrapar a cada uno de los tipos de las subclases
de manera individual si estas requieren ser procesadas diferente.
Limpieza de
pila.
En
ocasiones cuando se lanza una excepción, pero no se atrapa en un enlace
específico, la pila de llamadas se limpia y el programa intenta volverlo a
atrapar en el siguiente bloque, esto se conoce como limpia de pila. Este
proceso hace que el método en el que no se atrapó la excepción termina, todas
sus variables quedan fuera del enlace y el control regresa a la instrucción que
originalmente la invocó. La limpieza de pila de repetirá hasta que la excepción
pueda ser atrapada porque de lo contrario se producirá un error a la hora de
compilar.
Aserciones.
Las
aserciones ayudan a asegurar la validez del programa al atrapar los errores
potenciales e identificar los posibles errores lógicos del desarrollo. Estas
pueden escribirse como comentarios para apoyar a la persona que desarrolla el
programa. Algunos ejemplos son:
Precondiciones
y pos condiciones ñ Estas características son utilizadas por los programadores
para hacer un análisis de lo esperado del programa antes y después de su
ejecución. Son importantes porque gracias a ellas se pueden detectar posibles
fallas en el programa y corregirlas.
Las
precondiciones son verdaderas cuando se invoca a un método, estas describen las
características del método y las expectativas que se tienen en el estado actual
del programa. Si no se cumplen las precondiciones el comportamiento del método
es indefinido por lo que se lanza una excepción que esté preparada o continuar
con el programa esperando el error. Las pos condiciones describen las restricciones
en el entorno y cualquier efecto secundario del método. Es recomendable
escribirlas para saber que esperar en un futuro si es que se hacen
modificaciones.
Conclusión.
El manejo
de excepciones ayuda a lidiar con los errores de una aplicación por medio de la
manipulación del código para hacer programas más robustos. Además existen
herramientas que ayudan a manejarlas tal es el caso de los bloques try (intentar) que encierran el
código que puede lanzar una excepción y los bloques el catch (atrapar) que lidian con
las excepciones que surjan. También existen técnicas que el programador utiliza
para conocer el posible funcionamiento del programa y detectar los errores que
pueda contener.
Ejemplo:
import java.io.IOException;
// ...
public static void main(String[]
args) {
try {
// Se ejecuta algo que puede producir
una excepción
} catch (IOException e) {
// manejo de una excepción de
entrada/salida
} catch (Exception e) {
// manejo de una excepción cualquiera
} finally {
// código a ejecutar haya o no
excepción
}
}
Básicamente el manejo de excepción es un
Objeto descendiente de la clase java.lang.Object, podemos pensar en ellas como
una condición excepcional en nuestro sistema el cual altera la correcta
ejecución del mismo, las excepciones nos indican que hay algo anómalo, inconsistente o simplemente un Error, lo cual
impide que el sistema se ejecute como debería de ser...
Tal vez se preguntaran si ¿pero Anómalo,
inconsistente o Error no es básicamente lo mismo?...... podría ser, pero en
este enfoque no necesariamente lo es, ya que lo que vamos a conocer como una
excepción no siempre es un error (hablando como excepcion en general, ya que en
java una Excepción es muy diferente a un Error), muchas veces necesitaremos
trabajar con excepciones controladas para indicar alguna inconsistencia en
nuestro sistema que podría provocar errores.......
A modo de ejemplo, podemos encontrarnos
con el famoso NullPointerException el cual nos indica que un objeto se
encuentra vació, pero esto no es un error ya que nosotros podemos trabajar con
objetos null, entonces veámoslo como si la excepcion nos dijera "Es un
objeto nulo y no se puede efectuar el proceso", mientras que hay errores
como NoClassDefFoundError el cual nos indica que la máquina virtual de Java
(JVM) no puede encontrar una clase que necesita, debido a por ejemplo que no
encuentra un .class, esto si se maneja como un error en java.
Jerarquía de excepciones:
No hay comentarios:
Publicar un comentario