Saltar a contenido

Polimorfismo

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 sobreescritura de métodos y las conversiones entre clases de la jerarquía sientan las bases para el polimorfismo. Este 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 deben estar redefinidos en las clases derivadas.
  • Los objetos deben ser manipulados utilizando referencias a la clase base.

Sobrescritura de métodos

Los métodos heredados de la clase base pueden ser objeto de sobreescritura u override en las clases derivadas. El método en la clase derivada se debe escribir con el mismo nombre, el mismo número y tipo de parámetros y el mismo tipo de retorno que en la clase base. Si no fuese así estaríamos sobrecargando el método, no redefiniéndolo.

    public void cambiarDatos (String m, String c){
        marca = m;
        color = c;
    }
    public void cambiarDatos (String m){
        marca = m;
    }
    // método en clase base
    public void cambiarDatos (String m, String c){
        marca = m;
        color = c;
    }

    // método en clase derivada
    public void cambiarDatos (String m, String c){
        marca = m;
        if(m.equals("Peugeot")){
            color = "Blanco";
        }else{
            color = c;
        }
    }

Sobreescribiendo métodos heredados

El método sobrescrito puede tener un modificador de acceso menos restrictivo que el de la clase base. Si por ejemplo el método heredado es protected, se puede redefinir como public pero no como private porque sería una acceso más restrictivo que el que tiene en la clase base. Cuando en una clase derivada se redefine un método de una clase base, se oculta el método de la clase base y todas las sobrecargas del mismo en la clase base. Para esto se hace uso de la palabra reservada override.

class Pelicula{
    private String titulo;

    public Pelicula (String titulo){
        this. titulo = titulo;
    }

    public String trama (){
        return "No tiene trama";
    }
}

class Spiderman extends Pelicula{
    public Spiderman(){
        super("Spiderman");
    }

    @Override
    public String trama(){
        return "Un hombre adquiere poderes tras ser mordido por una araña."
    }
}

Gracias al polimorfismo tenemos la capacidad de llamar al método trama con el mismo tipo de objeto, en este caso de tipo Pelicula y obtener diferentes resultados, ya que Java automáticamente ve qué tipo de hijo es e imprime el correspondiente método trama. Para el caso de que no se haya sobreescrito el método llamará al de la clase padre.

No podemos asignar un objeto de referencia de padre a una variable de clase hijo. Si queremos convertir un padre en hijo, la variable tiene que ser creada de tipo hijo. Si queremos convertir un hijo en padre tendremos que hacer un Upcasting, y al revés tendríamos un Downcasting.

Herencia
Perro pe = new Animal(); // No compila
Perro p = (Perro) new Animal() // Compila pero da error de ejecución
Animal a = new Perro();
Perro pe = (Perro) a;
Animal a = (Animal) new Perro();

Métodos y clases abstractos y finales

La abstracción es un proceso de ocultar los detalles de implementación y mostrar solo la funcionalidad al usuario. Una clase abstracta es una clase que no se puede instanciar pero que puede ser el padre de otras clases. Aunque no se puede instanciar, una clase abstracta define métodos y variables que heredan las clases hijas.

Clases abstractas

Una clase abstracta es una clase que no se puede instanciar, es decir, no se pueden crear objetos de esa clase. Se diseñan solo para que otras clases hereden de ellas. La clase abstracta normalmente es la raíz de una jerarquía de clases y contendrá el comportamiento general que deben tener todas las subclases. Las clases abstractas pueden:

  • Pueden contener cero o más métodos abstractos.
  • Pueden contener métodos no abstractos.
  • Pueden contener atributos.

Todas las clases que hereden de una clase abstracta deben implementar todos los métodos abstractos heredados. Si una clase derivada de una clase abstracta no implementa algún método abstracto se convierte en abstracta y tendrá que declararse como tal (tanto la clase como los métodos que siguen siendo abstractos).

Las clases abstractas pueden opcionalmente contener métodos abstractos. También pueden contener métodos no abstractos, que serán heredados por los hijos. Un método abstracto no tiene cuerpo (No tiene código). Solo se escribe la signatura del método con la palabra reservada abstract.

public abstract class Animal {

    protected String nombre;

    public Animal(String nombre) {
        this.nombre = nombre;
    }

    public abstract void alimentar();
    public abstract void mover();

    public String getNombre() {
        return nombre;
    }
}
public class Dog extends Animal {

    public Dog(String nombre) {
        super(nombre);
    }

    @Override
    public void alimentar() {
        System.out.println("El perro " + getNombre() + " está comiendo.");
    }

    @Override
    public void mover() {
        System.out.println("Se está moviendo");
    }
}

Un hijo abstracto de un padre abstracto no tiene que definir métodos no abstractos para los métodos abstractas que hereda. Esto significa que puede haber varios pasos entre una clase base abstracta y una clase secundaria que no es completamente abstracta. Es decir, el hijo solamente está obligado a implementar los métodos abstractos que tenga el padre.

No todo lo definido en una clase abstracta debe ser abstracto. Sin embargo, si una clase contiene incluso un método abstracto, entonces la clase en sí debe declararse abstracta.

¿Para qué se usan las clases abstractas?

Las clases abstractas se utilizan para organizar programas. Agrupar las clases es importante para mantener un programa organizado y comprensible. La ventaja de usar una clase abstracta es que puede agrupar varias clases relacionadas como hermanas.

Aunque no se puedan instanciar las clases abstractas también poseen constructores. La mayoría de las veces se utilizan cuando quieres realizar alguna inicialización de los campos de la clase abstracta antes de la instanciación de una clase hija.

Actividad

  • AC 709 (RA7/ CE7a CE7b CE7c CE7d CE7e CE7f CE7g CE7h CE7j / IC1 / 3p). Escribe un programa con las siguientes clases:

    • Una clase principal Herencia2 que contendrá el método main.
    • Un clase abstracta Vehiculo con los atributos no públicos pero heredables de tipo entero num_bastidor y velocidad, y los métodos abstractos públicos acelerar y frenar.
    • Las clases Bicicleta y Patinete. Estas clases heredarán de Vehiculo num_bastidor y velocidad e implementarán los métodos acelerar y frenar.
    • El programa deberá presentar un menú con las siguientes acciones:
      1. Crear un vehículo Bicicleta (b) o Patinete eléctrico (p)
      2. Acelerar el vehículo incrementando la velocidad en cierta cantidad.
      3. Frenar el vehículo decrementando la velocidad en cierta cantidad.
      4. Salir del programa.
    • En a se debe solicitar el número de bastidor y preguntar al usuario si se genera una bicicleta (b) o un patinete (p). Entonces mostrará el número de bastidor y la velocidad.
    • En b se debe preguntar al usuario por una cantidad. En caso de que se haya creado un patinete deberá incrementarse la velocidad en el doble a lo indicado, y mostrar la nueva velocidad por pantalla.
    • En c se debe solicitar por teclado una cantidad. Si se ha creado un patinete deberá decrementar la velocidad a la mitad de lo indicado, y mostrar la nueva velocidad por pantalla.