El polimorfismo es otro de los pilares de la programación orientada a objetos, junto al encapsulamiento, la abstracción, y la herencia. Está completamente ligado al mecanismo de herencia.
Básicamente consiste en acceder a métodos de una subclase mediante un tipo de datos de su superclase.
Viene del término de biología en el que un organismo puede tener diferentes formas. En programación orientada a objetos podemos tener aparentemente un mismo objeto que se comporta de formas distintas.
De una forma resumida se puede decir que consiste en realizar un casting entre objetos: una variable de una superclase puede almacenar referencias de objetos de sus subclases. Podemos usar una variable de una superclase para acceder desde ella a métodos sobrescritos en la subclase.
Para explicar esto vamos a crear 3 clases: Vehiculo (Superclase), Coche y Moto (Subclases)
public Class Vehiculo{ private int numRuedas; private int potencia; private String matricula; ... public void mostrarEspecficaciones(){ System.out.println("Especificaciones del vehiculo:"); System.out.println("Cantidad de ruedas: " + numRuedas); System.out.println("CV de Potencia: " + potencia); System.out.println("Nº de Matricula: " + matricula); } }
A continuación creamos dos subclases: Coche y Moto
//Clase Coche public class Coche extends Vehiculo{ private int numPuertas; ... @Override public void mostrarEspecficaciones(){ super.mostrarEspecificaciones(); System.out.println("Nº de puertas del coche: " + numPuertas); System.out.println("El Vehiculo es un coche"); } } //Clase Moto public class Moto extends Vehiculo{ private String marca; ... @Override public void mostrarEspecficaciones(){ super.mostrarEspecificaciones(); System.out.println("Marca de la moto: " + marca); System.out.println("El Vehiculo es una moto"); } }
Ahora que ya tengo las 3 clases creadas, vamos a ver en qué consiste:
//Creo un vehiculo con matricula, potencia y ruedas Vehiculo vehiculo1 = new Vehiculo("FWD-2343",102, 3); //Creo otro vehiculo de subtipo Moto Vehiculo vehiculo2 = new Moto("ASD-1234", 160, 2, Yamaha); //Creo otro vehiculo de subtipo Coche Vehiculo vehiculo3 = new Coche("FGH-5678", 200, 4, 5); //Ejecuto el mismo método para los 3 vehiculos //El mismo método actúa de forma distinta vehiculo1.mostrarEspecificaciones(); //ejecuta el método de la clase Vehiculo vehiculo2.mostrarEspecificaciones(); //ejecuta el método de la clase Moto vehiculo3.mostrarEspecificaciones(); //ejecuta el método de la clase Coche //Intento acceder a métodos de las subclases vehiculo2.getMarca(); vehiculo3.getNumPuertas() //No es posible ya que el tipo Vehiculo no tiene esos métodos
Se puede llamar al método mostrarEspecificaciones()
ya que es un método de la clase Vehículo. Cuando se ejecuta ese método primero se busca si existe en el objeto que tengo guardado en la variable, y si no existe, buscaré en el de la superclase, y así sucesivamente, hasta la clase Object.
Sin embargo, si desde una variable de la superclase Vehiculo
quiero acceder a algún método de alguna subclase (getNumPuertas() ó getMarca()), no se permite, ya que no están definidos en esa clase.
Tampoco se permite almacenar un objeto de la superclase en una variable de una subclase:
Coche coche = new Vehiculo("ERT-653", 90, 4); //Error de compilación
Como hemos visto, podemos referenciar objetos de una subclase mediante variable de un superclase.
Vehiculo vehiculo1; vehiculo1 = new Coche();
Esto se conoce como UpCasting, o conversión hacia arriba (desde una subclase a una superclase). Es un casting implícito y no es necesario indicar ninguna instrucción para realizar la conversión.
Cualquier objeto se puede castear implícitamente a una variable de tipo Object
, ya que es la superclase de cualquier otra clase.
Con el UpCasting ganamos generalización, pero perdemos información acerca de los subtipos que casteamos (Coche en este caso). Ya no podemos acceder a los métodos miembro de la clase Coche, porque no están definidos en la superclase Vehiculo.
Si se quiere recuperar la información de una subclase cuando tenemos una variable de la superclase, será necesario moverse hacia abajo en la jerarquía mediante una conversión de tipo.
Esta conversión a un tipo más específico (subclase) desde un tipo más general, se conoce como DownCasting o conversión hacia abajo, y se realiza mediante una operación de casting:
//UpCasting Vehiculo vehiculo1 = new Coche(); Vehiculo vehiculo2 = new Moto(); //No puedo llamar al método de la clase Coche vehiculo1.getNumPuertas(); //DownCasting Coche miCoche = (Coche) vehiculo1(); Moto miMoto = (Moto) vehiculo2(); //Puedo volver a llamar a sus métodos miembro miCoche.getNumPuertas(); miMoto.getNumPuertas(); //También se permite ((Coche)vehiculo1).getNumPuertas(); ((Moto)vehiculo2).getMarca();
Como acabamos de ver, es posible que una variable de referencia de un tipo más general (Vehiculo) pueda contener la referencia de algún subtipo (subclase de Vehiculo) de objeto.
Esto nos facilita las cosas cuando trabajamos con distintas subclases, ya que podemos tratarlas como un elemento de la superclase:
ArrayList<Vehiculo> listaVehiculos = new ArrayList<Vehiculo>(); listaVehiculos.add(new Coche()); listaVehiculos.add(new Moto()); listaVehiculos.add(new Camion()); listaVehiculos.add(new Avion());
Pero como también hemos dicho, puede que perdamos información acerca de cada objeto almacenado.
El operador instanceof
nos indica el tipo de objeto que tenemos en una variable de la superclase:
for(Vehiculo vehiculo : listaVehiculos){ if(vehiculo instanceof Coche){ //Lo gestiono como coche Coche coche = (Coche)vehiculo; }else if(vehiculo instanceof Moto){ //Lo gestiono como moto Moto moto = (Moto)vehiculo; }else if(vehiculo instanceof Camion){ //Lo gestiono como camion Camion camion = (Camion)vehiculo; }else if(vehiculo instanceof Avion){ //Lo gestiono como avion Avion avion = (Avion)vehiculo; } }
También tenemos el método getClass()
heredado desde la clase Object
que nos aporta una funcionalidad parecida.
if(vehiculo.getClass() == Coche.class){ System.out.println("Es un coche"); }
© 2024 Fernando Valdeón