POLIMORFISMO (OBJETIVO 5.2):
- Una variable de referencia es siempre (e inalterable) de un solo tipo, pero puede referirse a un subtipo de objeto.
- El tipo de la variable de referencia (no el tipo del objeto), determina cuales pueden ser los métodos invocados.
- Las invocaciones a métodos polimórficos se realizan solo a los métodos de instancia sobreescritos
SOBREESCRITURA Y SOBRECARGA (OBJETIVO 1.5 Y 5.4):
Con respecto al método el método sobreescrito(Override):
- Debe poseer el mismo tipo de retorno, excepto a partir de Java 5, en donde el tipo de retorno puede ser una subclase (a esto se lo denomina como retorno covariante).
- No debe poseer un modificador de acceso más restrictivo.
- Puede poseer un modificador de acceso menos restrictivo.
- No debe lanzar nuevas o mayores (en relación al árbol de herencia) excepciones comprobadas.
- Puede lanzar menos o menores (en relación al árbol de herencia) excepciones comprobadas, o cualquier excepción no comprobada
Con respecto al método que sobrecargado:
- Pueden poseer diferentes tipos de retorno, si las listas de argumentos son diferentes.
- Pueden poseer diferentes modificadores de acceso.
- Pueden lanzar diferentes excepciones.
Ejemplos ilegales de Métodos Override(ninguno compila)
public class Animal{ public void eat(){ } }
Código de Sustitución Ilegal | Problema con el código |
---|---|
private void eat(){ } | El modificador de acceso es mas restrictivo |
public void eat() throws IOException { } | Declara una excepción marcada que no está definida en al versión de la superclase |
public void eat(String food){ } | Una sobrecarga legal pero no un override porque la lista de argumentos ha cambiado |
public String eat() { } | No es un override porque el tipo de retorno es diferente. No es una sobrecarga tampoco porque no ha cambiado la lista de argumentos |
- Los métodos pueden ser sobreescritos o sobrecargados; los constructores pueden ser sobrecargados pero no sobreescritos.
- El polimorfismo se encuentra en la sobreescritura, no en la sobrecarga.
- El tipo de un objeto (no el tipo de la variable de referencia), determina que método sobreescrito es utilizado en tiempo de ejecución.
- El tipo de la referencia determina que método sobrecargado se utilizará en tiempo de complicación.
Para resumir, cuando se trata de la llamada a un método sobreescrito, este es decidido en tiempo de ejecución basándose en el tipo de objeto, pero la versión sobrecargada del método a llamar está basado en el tipo de referencia del argumento pasado en tiempo de compilación.
POLIMORFISMO EN MÉTODOS SOBRECARGADOS Y SUSTITUIDOS
A veces, un método está sobrecargado y sustituido. Imagina una clase Animal y una clase Horse que son así:
class Animal{ public void eat(){ System.out.println("Animal genérico comiendo"); } } public class Horse extends Animal{ public void eat(){ System.out.println("Horse comiendo paja"); } public void eat(String s){ System.out.println("Hose comiendo "; + s); } }
Código de Invocación del método | Resultado |
---|---|
Animal a = new Animal() a.eat() | Animal genérico comiendo |
Horse h = new Horse() h.eat() | Horse comiendo paja |
Animal ah = new Horse() ah.eat() | Horse comiendo paja. Polimorficamente funciona. El tipo de objeto (Horse) no el tipo de referencia (Animal) es usado para determinar que método eat() se llama. |
Horse he = new Horse() he.eat(“Manzanas”) | Horse comiendo Manzanas El método sobrecargado eat(String s) es invocado |
Animal a2 = new Animal() a2.eat(“Treats”) | Error de compilación. El compilador ve que la clase Animal no tiene un método eat() que coja un String. |
Animal ah2 = new Horse() ah2.eat(“Carrots”) | Error de Compilación. El compilador se mantiene mirando solo la referencia y ve que Animal no tiene ningún método eat() que coja un String. |
Método Sobrecargado(Overload) | Método Sustituido(Override) | |
---|---|---|
Argumentos | Deben cambiar | No deben cambiar |
Tipo de retorno | Puede cambiar | No puede cambiar excepto retorno covariante |
Excepciones | Puede cambiar | Puede reducir o eliminar. No puede ser lanzada ninguna nueva o mas amplia |
Acceso | Puede cambiar | No debe hacerlo mas restrictivo (Pero si ser menos restrictivo) |
Invocación | El tipo de referencia determina cual versión sobrecargada es seleccionada. Ocurre en tiempo de compilación. |
El tipo de objeto determina cual de los métodos es llamado |
CONVERSIÓN DE VARIABLES DE REFERENCIA (OBJETIVO 5.2):
- Existen dos tipos de conversión de variables de referencia: especificación (downcasting) y generalización (upcasting).
- Downcasting: Con una referencia a un supertipo de objeto, se puede realizar una conversión explícita a un subtipo, para acceder a los miembros de ese subtipo.
- Upcasting: Con una referencia a un subtipo de objeto, se puede acceder explícita o implícitamente a los miembros de la clase base.
class Animal{ } class Dog extends Animal { } class DogTest{ public static void main(String[] args){ //upcasting Dog d = new Dog(); Animal a1 = d; // Upcast implícito Animal a2 = (Animal) d; // o también explícito //downcasting Animal animal = new Dog(); Dog d1 = (Dog) animal; // cast solo explícito } }
TIPOS DE RETORNO (OBJETIVO 1.5):
- Los métodos con referencias a objetos como tipo de retorno, pueden devolver subtipos.
Los métodos con una interface como tipo de retorno, pueden devolver cualquier instancia de una clase que la implemente.
class Alpha{ Alpha doStuff(char c){ return new Alpha(); } } class Beta extends Alpha{ Beta doStuff (char c){ //Sustitucion legal en Java 1.5 return new Beta(); } }
En un método con un tipo de retorno primitivo, podemos retornar cualquier valor o variable que pueda ser implícitamente convertida al tipo de retorno declarado.
public int foo(){ char c = 'c'; return c; // char es compatible con int }
En un método con un tipo de retorno primitivo, podemos retornar cualquier valor o variable a la que pueda hacerse cast al tipo de retorno declarado.
public int foo(){ float f = 32.5f; return (int) f; }
CONSTRUCTORES E INSTANCIACIÓN (OBJETIVO 1.6 Y 5.4):
- Los constructores pueden utilizar cualquier modificador de acceso.
- La primera línea de todos los constructores debe ser this() (si es un constructor sobrecargado) o super().
- Si una superclase no posee un constructor sin argumentos, es aconsejable crear un constructor sin argumentos el cuál invoque a alguno de los constructores existentes pasando parámetros por defecto.
- Los constructores nunca son heredados, y por lo tanto no pueden ser sobreescritos.
- Las invocaciones a this() y super() no pueden estar en un mismo constructor. Se puede tener uno u otro, pero nunca ambos.
ESTÁTICOS (OBJETIVO 1.3):
- Los métodos estáticos son utilizados para implementar comportamientos que no están afectados por el estado de ninguna instancia.
- Un método estático no puede acceder a una variable de instancia directamente.
- Los métodos estáticos no pueden ser sobreescritos, pero si pueden ser redefinidos.
class Animal { static void doStuff() { System.out.print("a "); } } class Dog extends Animal { static void doStuff() { // se está redifiniendo, // NO override System.out.print("d "); } public static void main(String[] args) { Animal[] a = { new Animal(), new Dog(), new Animal() }; for (int x = 0; x < a.length; x++) a[x].doStuff(); // invocando al metodo estatico } }
Salida del programa
a a a
ACOPLAMIENTO Y COHESIÓN (OBJETIVO 5.1):
- El acoplamiento se refiere al grado en que una clase conoce o usa los miembros de otra clase.
- Débil acoplamiento es el estado deseado para tener clases que estén bien encapsuladas, minimizadas las referencias entre ellas, y se limitan al uso de las API?s.
- Fuerte acoplamiento es el estado indeseado de tener clases que no respetan las reglas de bajo acoplamiento.
- La cohesión se refiere al grado en que una clase, cumple un rol bien definido o tiene una responsabilidad específica.
Alta cohesión es el estado deseado de una clase que posee un rol bien definido. - Baja cohesión es el estado indeseado de una clase que posee muchos roles y demasiadas responsabilidades.