24 de enero de 2019

Herencia

Ya he adelantado que las clases pueden heredar de otras clases. Esta capacidad de heredar nos va a servir para que clases relacionadas que deben tener miembros parecidos o repetidos no tengan que ser implementados para cada clase. Si una clase superior (superclase o clase padre) tiene una serie de miembros implementados, pueden ser aprovechados por clases que hereden de ella (subtipo o clase hija).

Con la herencia reutilizamos código de clases fuertemente relacionadas, estableciendo una relación "es un" o de especialización


Se van a heredar todos los miembros (no los constructores), pero su visibilidad lo regirá el control de acceso que marquemos (un miembro private no se podrá ver desde su heredero como ya vimos, deberá ser marcado mínimo como protected para tener acceso desde un heredero)

Para poder definir y usar la herencia vamos a usar las palabras reservadas extends y super:
  • extends: sirve para definir la clase de la que se hereda (superclase) en la propia cabecera de la clase que hereda (subtipo).
  • super: sirve para referirnos a miembros de la superclase (parecido a como hacíamos con this)
Vamos a verlo con el código. En nuestro ejemplo vamos a crear una clase padre con nombre Vehiculo que nos sirva también para aeronaves, embarcaciones, etc... y decidimos que lo que deben tener todos los vehículos es un modelo y color. La forma de hacerlo es extraer los miembros que tienen que ver con modelo y color y moverlos a nuestra nueva clase Vehiculo.

Lo más fácil usando Eclipse es usar las ayudas que nos ofrece este IDE. Si hacemos click derecho sobre la Coche (en el Package Explorer o directamente en la ventana de su código fuente) elegimos Refactor > Extract Superclass... y en la ventana que nos aparece le ponemos el nombre Vehiculo y elegimos en la parte de abajo los miembros que vamos a extraer. Estos son: modelo, color, getColor(), setColor(String). Al clickar en Finish tendremos nuestra superclase creada (ver en su fichero .java).
public class Vehiculo {

    protected String modelo;
    String color;

    public Vehiculo() {
        super();
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

}
Ahora vemos que Coche carece de esos miembros (en su código ya no aparecen) y lo que debemos hacer es declarar que herede de Vehiculo con la palabra reservada extends. Eclipse ya ha hecho ese trabajo por nosotros. Si hacemos la refactorización a mano no nos podemos olvidar de declarar esta herencia:
public class Coche extends Vehiculo {

  // Codigo sin los miembros de Vehiculo

}
Ahora sí que son miembros que conoce la clase Coche. Y los podremos usar de la misma forma que ya los usábamos. Puedes comprobarlo con el código que hicimos en la sesión de igualdad de objetos.

De hecho vamos a cambiar un poco el código para ver cómo los valores de tipo Coche SON también un Vehiculo:
public class Igualdad {

  public static void main(String[] args) {
    Coche coche1 = new Coche("Seat Ibiza", "Rojo");
    coche1.setMatricula("1234 BBB");
    //...
    Vehiculo coche3 = coche1;
    // coche3.setMatricula("5678 CCC");// error!

    //...
  }
}
Aquí vemos que coche1 puede asignarse a una variable de tipo Vehiculo y el resto de código funciona correctamente. Es una buena práctica acostumbrarse a usar los tipos más básicos de lo que necesito: como sólo uso los miembros de modelo y color, me sirve un tipo Vehiculo. Sin embargo si quisiera usar el método setMatricula(String) sí que tendría que usar una variable de tipo Coche, porque sólo así tendré acceso a ese método como ya os adelantaba en la sesión donde construíamos objetos. Queda demostrado entonces que las variable dan acceso a los miembros de su tipo, independientemente de que el valor sea de un subtipo más especializado.

Con la herencia se crea una jerarquía de tipos más especializados compatibles con sus ancestros


Aprovechando los constructores

Hemos dicho que se heredan los miembros pero no los constructores: ¿Cómo aprovecho el código que ya tenía en los constructores?

Lo que hacemos es acceder a miembros de las clases superiores o sus constructores. Voy a crearme un constructor de Vehiculo con los dos parámetros modelo y color:
public Vehiculo(String modelo, String color) {
    this.modelo = modelo;
    this.color = color;
}
Ahora puedo llamar al constructor de la superclase para aprovecharlo y sólo añadir código nuevo. En mi caso uso el constructor de Coche con modelo y color, pero voy a vaciarlo del código repetido ahora en Vehiculo. Nuestro constructor pasa de esto:
public Coche (String color, String modelo) {
    super(color, modelo);
    numeroDeRuedas = 4;
}
NOTA: Asigno 4 a numeroDeRuedas pues ya no utiliza el constructor por defecto Coche(). Te propongo que modifiques los otros constructores para que reutilicen éste como vimos en la sesión sobre constructores.


Con la palabra reservada super hacemos referencia a la superclase (igual que con this hacemos referencia a sí mismo). Si usamos el asistente de contenido veremos que nos salen los miembros de la superclase a los que tengo acceso: modelo, color, getColor(), setColor(String) y todos los de Object que es "el abuelo".

Con lo que hemos visto aquí ya tenemos nuestra primera jerarquía de clases. En la siguiente sesión sobre clases abstractas vamos a hacernos otra clase Moto que también herede de Vehiculo, pero en medio, para reutilizar el código que tenga que ver con el número de ruedas, crearemos la clase abstracta VehiculoConRuedas.

No hay comentarios:

Publicar un comentario

Compárteme

Entradas populares