3 Polimorfismo¶
Casting de objetos¶
Como ocurre con los tipos primitivos, también es posible realizar casting entre objetos siempre y cuando esté dentro de la estructura jerárquica en su herencia.
Para crear un objeto, hay que declarar una variable cuyo tipo es una clase:
Eso significa que vehicle es de tipo Vehicle y que nunca va a cambiar de tipo, siempre va a ser de tipo Vehicle
Creemos ahora una variable de tipo Car:
Lo mismo que antes, car es una variable de tipo Car y nunca va a cambiar de tipo, siempre va a ser de tipo Car.
A la variable vehicle se le puede asignar un objeto de tipo Vehicle y también se le puede asignar objetos cuyo tipo sean una subclase de Vehicle:
vehicle = car; // A vehicle se le está asignando un objeto de tipo Car, que es una subclase de Vehicle
La variable vehicle contiene un objeto de tipo Car pero su tipo es Vehicle, entonces solamente podrá acceder a los atributos y métodos de Vehicle:
La variable vehicle no puede acceder al método refuel porque dicho método está definido en la clase Car. Es decir, una variable padre puede contener un objeto de tipo hijo pero solamente podrá acceder a los atributos y métodos definidos en el padre.
¿y si la asignación la hiciéramos al revés, es decir, a car le asignamos vehicle?
Daría un error de coincidencia de tipos, pero se podría solucionar con un casting:
Hay que tener en cuenta que para que el casting funciones, la variable vehicle debe contener objeto de tipo Car, porque si no, dará un error de ejecución ClassCastException.
vehicle = new Vehicle(2, "blanco");
car = (Car) vehicle; // Error de ejecución: ClassCastException ya que Vehicle no contiene un objeto de tipo car
Modificador final¶
El modificador final tiene varios usos en función de dónde se utilice:
-
Delante de una variable en su declaración, crea una constante. La constante puede recibir el valor en tiempo de compilación o en tiempo de ejecución.
-
Delante de una variable que referencia a un objeto: dicha variable no puede referenciar a otro objeto.
-
En la declaración de un método: dicho método no se puede anular por las subclases:
- En la definición de una clase: significa que ese clase no puede tener descendencia.
Polimorfismo¶
Polimorfismo es la capacidad de un objeto de adquirir varias formas.
La sobrecarga de métodos es un tipo de polimorfismo estático porque se resuelve en tiempo de compilación el método apropiado a ser llamado basado en la lista de argumentos.
La anulación de métodos es un tipo de polimorfismo dinámico porque se resuelve en tiempo de ejecución atendiendo al tipo del objeto. Con una variable de tipo padre, si se utiliza un objeto del padre para invocar al método, entonces se ejecutará el método de la clase padre, pero si se utiliza un objeto de la clase hija para invocar al método, entonces se ejecutará el método de la clase hija
public class Polimorfismo {
public void show(){
Vehicle vehicle = new Vehicle(2, "azul");
vehicle.accelerate(100.39); // Método del padre
System.out.printf("La velocidad del vehículo es %.2f km/h\n", vehicle.getSpeed());
vehicle = new Car(4, "rojo");
vehicle.accelerate(50.89); // Método del hijo
System.out.printf("La velocidad del vehículo es %.2f km/h\n", vehicle.getSpeed());
}
public static void main(String[] args) {
new Polimorfismo().show();
}
}
Otro de las ventajas del polimorfismo es poder tener un método que solicite un padre, y poder pasarle un hijo:
public class Polimorfismo2 {
public void show(){
accelerateVehicle(new Car(4, "rojo"));
}
private void accelerateVehicle(Vehicle vehicle) {
vehicle.accelerate(100.39); // Método del padre
System.out.printf("La velocidad del vehículo es %.2f km/h\n", vehicle.getSpeed());s
}
public static void main(String[] args) {
new Polimorfismo2().show();
}
}
instanceof¶
El operador instanceof permite comprobar si un determinado objeto pertenece a una clase concreta. Se utiliza de esta forma:
Devuelve true si el objeto pertenece a dicha clase.
public class InstanceOf {
public void show(){
Vehicle vehicle = new Vehicle(2, "azul");
Car car = new Car(4, "rojo");
System.out.println(vehicle instanceof Vehicle); // true
System.out.println(vehicle instanceof Car); // false
System.out.println(car instanceof Vehicle); // true
System.out.println(car instanceof Car); // true
}
public static void main(String[] args) {
new InstanceOf().show();
}
}
Tal y como podemos observar en el ejemplo, car también devuelve true con Vehicle ya que los objetos de las subclases también devuelven true con la superclase.
toString¶
La clase Object es la clase raíz de todo el árbol de la jerarquía de clases Java, es decir, es una superclase implícita de todas las demás clases. En otras palabras, todas las demás clases son subclases de Object. Esto significa que una variable de referencia de tipo Object puede referirse a un objeto de cualquier otra clase.
La clase Object proporciona un cierto número de métodos de utilidad general que pueden utilizar todos los objetos ya que los heredan. Pero normalmente hay que sobrescribirlos para que funcionen adecuadamente adaptándolos a la clase correspondiente. Esto se hace con la idea de que todas las clases utilicen el mismo nombre y prototipo de método para hacer operaciones comunes. Como por ejemplo, toString()
que se utiliza para obtener una cadena de texto que represente al objeto. El método toString() de la clase Object devuelve una cadena que consiste en el nombre de la clase del objeto, el carácter arroba ‘@’ y la representación hexadecimal sin signo del código hash del objeto. Siempre se recomienda sobrescribir el método toString() para obtener nuestra propia representación del objeto.
public class Vehicle{
//...
@Override
public String toString() {
return "Vehicle [wheelCount=" + wheelCount+ ", speed=" + speed + ", color=" + color + "]";
}
}
Algunos entornos IDEs, puede autogenerar el código del método toString.
Haciendo uso del método para sacar por consola un objeto no es necesario hacer toString de forma explícita, ya que dicho método lo llama de forma implícita.